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)
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)
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
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
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
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
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
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
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"]
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()]
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"]