def do_logout(environ, start_response, server_env, state): """ Get a request """ logger.info("--- LOGOUT ---") _dict, binding = unpack_any(environ) logger.debug("Binding: %s, _dict: %s" % (binding, _dict)) resp = None if binding == BINDING_HTTP_ARTIFACT: resp = ServiceError("Artifact support not yet implemented") elif binding == BINDING_URI: resp = BadRequest("Binding not applicable") if resp: return resp(environ, start_response) try: request = _dict["SAMLRequest"] except KeyError: resp = BadRequest("Request missing") return resp(environ, start_response) try: req_info = server_env["idp"].parse_logout_request(request) req_info.binding = binding try: req_info.relay_state = _dict["relay_state"] except KeyError: pass logger.debug("LOGOUT request parsed OK") logger.debug("REQ_INFO: %s" % req_info.message) except KeyError, exc: logger.error("logout request error: %s" % (exc,)) resp = BadRequest("Erroneous logout request") return resp(environ, start_response)
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 sso_operation(self, msg, binding, uid=None): log.debug("Msg: " + str(msg)) log.debug("Inbound binding: " + str(binding)) if not (msg and "SAMLRequest" in msg): return BadRequest("Error parsing request or no request") else: if "Signature" in msg: try: kwargs = { "signature": msg["Signature"], "sigalg": msg["SigAlg"] } except KeyError: return BadRequest( "Signature Algorithm specification is missing") else: kwargs = {} try: kwargs["encrypt_cert"] = encrypt_cert_from_item( msg["req_info"].message) except KeyError: pass try: kwargs["relay_state"] = msg["RelayState"] except KeyError: pass if not uid is None: kwargs["uid"] = uid return self.do(msg["SAMLRequest"], binding, **kwargs)
def do(self, query, binding, relay_state="", encrypt_cert=None): 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 return_active_info(environ, start_response, server_env, state): logger.debug("[return_active_info]") try: req_info = get_authn_request(environ, server_env) except UnknownPrincipal: resp = BadRequest("Don't know the SP that referred you here") return resp(environ, start_response) except UnsupportedBinding: resp = BadRequest( "Don't know how to reply to the SP that referred you here") return resp(environ, start_response) except Exception: resp = BadRequest("Exception while parsing the AuthnRequest") return resp(environ, start_response) if req_info is None: # return error message resp = BadRequest("Missing SAMLRequest") return resp(environ, start_response) if req_info: session = state.old_session(req_info.sender()) if session: if req_info.message.force_authn: # even if active session session.reset() session["req_info"] = req_info start_response("302 Found", [("Location", "/")]) return [""] identity = session["identity"] if not identity: return NOT_AUTHN(environ, start_response, state, req_info) if not session or not session.active(): return NOT_AUTHN(environ, start_response, state, req_info) else: return NOT_AUTHN(environ, start_response, state, req_info) logger.debug("[return_active_info] Old session: %s" % session) identity = session["identity"] try: _eptid = session["eptid"] except KeyError: _eptid = get_eptid(server_env, req_info, session) session["eptid"] = _eptid identity["eduPersonTargetedID"] = _eptid authn_auth = session["authn_auth"] #def do_req_response(req_info, response, _environ, source, session, #service): resp = do_req_response(server_env, req_info, identity, environ, authn_auth, session) return resp(environ, 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): logger.debug("_operation: %s" % _dict) if not _dict: resp = BadRequest('Error parsing request or no request') return resp(self.environ, self.start_response) else: return self.do(_dict["SAMLRequest"], binding, _dict["RelayState"])
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 __call__(self, environ, start_response): path = environ.get('PATH_INFO', '').lstrip('/') print('P', path) logger.debug('<application> PATH: %r', path) if path == 'metadata': return sp.metadata(environ, start_response, _args) logger.debug("Finding callback to run") try: for regex, spec in self._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) func = getattr(cls, func_name) return func() else: return spec(environ, start_response, SP, CACHE) #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) return self._app(environ, start_response)
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: kwargs = { "signature": saml_msg["signature"], "sigalg": saml_msg["SigAlg"] } else: kwargs = {} 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, **kwargs) except KeyError: # Can live with no relay state return self.do(saml_msg["SAMLRequest"], binding, saml_msg["RelayState"], **kwargs)
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) 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, err: logging.error("StatusError: %s" % err) resp = BadRequest("%s" % err) return resp(environ, start_response)
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 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, exc: logger.error("Bad request: %s" % exc) resp = BadRequest("%s" % exc) return resp(self.environ, self.start_response)
def redirect(self): """ This is the HTTP-redirect endpoint """ logger.info("--- In SSO Redirect ---") _info = self.unpack_redirect() cert_str = None try: _key = _info["key"] _info = self.idphandler.idp_server.ticket[_key] self.req_info = _info["req_info"] del self.idphandler.idp_server.ticket[_key] except KeyError: if self.idphandler.copy_sp_cert: with lock: self.req_info = self.idphandler.idp_server.parse_authn_request( _info["SAMLRequest"], BINDING_HTTP_REDIRECT) cert_str = self.idphandler.idp_server.getvalid_certificate_str( ) else: self.req_info = self.idphandler.idp_server.parse_authn_request( _info["SAMLRequest"], BINDING_HTTP_REDIRECT) _req = self.req_info.message if "SigAlg" in _info and "Signature" in _info: # Signed request issuer = _req.issuer.text _certs = self.idphandler.idp_server.metadata.certs( issuer, "any", "signing") verified_ok = False for cert in _certs: if verify_redirect_signature(_info, cert): verified_ok = True break if not verified_ok: resp = BadRequest("Message signature verification failure") return resp(self.environ, self.start_response) _encrypt_cert = None if self.idphandler.copy_sp_key: _encrypt_cert = encrypt_cert_from_item(self.req_info.message) if self.user: if _req.force_authn: _info["req_info"] = self.req_info key = self._store_request(_info) return self.not_authn(key, _req.requested_authn_context, cert_str, _encrypt_cert) else: return self.operation(_info, BINDING_HTTP_REDIRECT) else: _info["req_info"] = self.req_info key = self._store_request(_info) return self.not_authn(key, _req.requested_authn_context, cert_str, _encrypt_cert) else: return self.operation(_info, BINDING_HTTP_REDIRECT)
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 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 slo_redirect_or_post(self, query, binding): log.debug("Query: " + query) log.debug("Binding: " + binding) try: req_info = self.parse_logout_request(query, binding) except Exception as exc: log.exception("Message parsing failed.") return BadRequest("Message parsing failed") msg = req_info.message if msg.name_id: lid = self.ident.find_local_id(msg.name_id) if lid in self.cache.user2uid: uid = self.cache.user2uid[lid] if uid in self.cache.uid2user: del self.cache.uid2user[uid] del self.cache.user2uid[lid] try: self.session_db.remove_authn_statements(msg.name_id) except KeyError as exc: log.exception("Session removal failed") resp = self.create_logout_response(msg, [binding]) binding, destination = self.pick_binding("single_logout_service", [binding], "spsso", req_info) response = True try: hinfo = self.apply_binding(binding, "%s" % resp, destination, query['relay_state'], response=response) except Exception as exc: log.exception("ServiceError: %s", exc) return ServiceError("%s" % exc) if binding == BINDING_HTTP_REDIRECT: for key, value in hinfo["headers"]: if key.lower() == "location": return Redirect(value, headers=hinfo["headers"]) return ServiceError("missing Location header") else: return Response(hinfo["data"], headers=hinfo["headers"])
def do(self, request, binding, relay_state="", encrypt_cert=None): 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 slo(environ, start_response, user): """ Expects a HTTP-redirect logout request """ query = None if "QUERY_STRING" in environ: logger.info("Query string: %s" % environ["QUERY_STRING"]) query = parse_qs(environ["QUERY_STRING"]) if not query: resp = Unauthorized('Unknown user') return resp(environ, start_response) try: req_info = IDP.parse_logout_request(query["SAMLRequest"][0], BINDING_HTTP_REDIRECT) logger.info("LOGOUT request parsed OK") logger.info("REQ_INFO: %s" % req_info.message) except KeyError, exc: logger.info("logout request error: %s" % (exc, )) resp = BadRequest('Request parse error') return resp(environ, start_response)
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)
# Either HTTP-Post or HTTP-redirect is possible, prefer HTTP-Post. # Order matters bindings = [BINDING_HTTP_POST, BINDING_HTTP_REDIRECT] try: response = IDP.create_logout_response(req_info.message, bindings) binding, destination = IDP.pick_binding("single_logout_service", bindings, "spsso", response) http_args = IDP.apply_binding(binding, "%s" % response, destination, query["RelayState"], response=True) except Exception, exc: resp = BadRequest('%s' % exc) return resp(environ, start_response) delco = delete_cookie(environ, "pysaml2idp") if delco: http_args["headers"].append(delco) if binding == BINDING_HTTP_POST: resp = Response(http_args["data"], headers=http_args["headers"]) else: resp = NotFound(http_args["data"], headers=http_args["headers"]) return resp(environ, start_response) def delete_cookie(environ, name): kaka = environ.get("HTTP_COOKIE", '')
req_info = server_env["idp"].parse_logout_request(request) req_info.binding = binding try: req_info.relay_state = _dict["relay_state"] except KeyError: pass logger.debug("LOGOUT request parsed OK") logger.debug("REQ_INFO: %s" % req_info.message) except KeyError, exc: logger.error("logout request error: %s" % (exc,)) resp = BadRequest("Erroneous logout request") return resp(environ, start_response) if not state.known_session(req_info.issuer()): resp = BadRequest("Logout request from someone I know nothing about") return resp(environ, start_response) # look for the subject subject = req_info.subject_id() logger.debug("Logout subject: %s" % (subject.text.strip(),)) status = None session = state.old_session(req_info.sender()) if session: session["authentication"] = "OFF" resp = do_logout_response(req_info, status) return resp(environ, start_response)
def sso(environ, start_response, user): """ Supposed to return a self issuing Form POST """ #edict = dict_to_table(environ) #if logger: logger.info("Environ keys: %s" % environ.keys()) logger.info("--- In SSO ---") query = None if "QUERY_STRING" in environ: if logger: logger.info("Query string: %s" % environ["QUERY_STRING"]) query = parse_qs(environ["QUERY_STRING"]) elif "s2repoze.qinfo" in environ: query = environ["s2repoze.qinfo"] if not query: resp = Unauthorized('Unknown user') return resp(environ, start_response) # base 64 encoded request # Assume default binding, that is HTTP-redirect req = IDP.parse_authn_request(query["SAMLRequest"][0]) if req is None: resp = ServiceError("Failed to parse the SAML request") return resp(environ, start_response) logger.info("parsed OK") logger.info("%s" % req) identity = dict(environ["repoze.who.identity"]["user"]) logger.info("Identity: %s" % (identity, )) userid = environ["repoze.who.identity"]['repoze.who.userid'] if REPOZE_ID_EQUIVALENT: identity[REPOZE_ID_EQUIVALENT] = userid # What's the binding ? ProtocolBinding if req.message.protocol_binding == BINDING_HTTP_REDIRECT: _binding = BINDING_HTTP_POST else: _binding = req.message.protocol_binding try: resp_args = IDP.response_args(req.message, [_binding]) except Exception: raise if req.message.assertion_consumer_service_url: if req.message.assertion_consumer_service_url != resp_args[ "destination"]: # serious error on someones behalf logger.error("%s != %s" % (req.message.assertion_consumer_service_url, resp_args["destination"])) resp = BadRequest("ConsumerURL and return destination mismatch") raise resp(environ, start_response) try: authn_resp = IDP.create_authn_response(identity, userid=userid, authn=AUTHN, **resp_args) except Exception, excp: logger.error("Exception: %s" % (excp, )) raise
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.info("<application> PATH: '%s'" % path) 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, ec_test = spec cls = callback(_sp, ec_test, environ, start_response, cache=CACHE) func = getattr(cls, func_name) return func() else: query = parse_qs(environ["QUERY_STRING"]) if "c" in query: ecs = query["c"][0] if ecs in SP: return spec(environ, start_response, SP[ecs]) return spec(environ, start_response, SP['']) if re.match(".*static/.*", path): return handleStatic(environ, start_response, path) if re.match(".*test", path) or path == "/" or path == "": resp = Response(mako_template="test.mako", template_lookup=LOOKUP, headers=[]) str_ec_seq = [] for ec in EC_SEQUENCE: str_ec_seq.append(str(ec)) argv = { # "ec_seq_json": json.dumps(EC_SEQUENCE), "ec_seq": str_ec_seq, "ec_info": EC_INFORMATION } return resp(environ, start_response, **argv) if re.match(".*overview", path): resp = Response(mako_template="test_overview.mako", template_lookup=LOOKUP, headers=[]) str_ec_seq = [] for ec in EC_SEQUENCE: str_ec_seq.append(str(ec)) argv = { # "ec_seq_json": json.dumps(EC_SEQUENCE), "ec_seq": json.dumps(str_ec_seq), "ec_info": json.dumps(EC_INFORMATION), "test_results": json.dumps(DB_HANDLER.get_overview_data()) } return resp(environ, start_response, **argv) return not_found(environ, start_response) except StatusError, err: logging.error("StatusError: %s" % err) resp = BadRequest("%s" % err) return resp(environ, start_response)
def bad_request(environ, start_response, msg): resp = BadRequest(msg) return resp(environ, start_response)