def active(self, subject_id, entity_id): """ Returns the status of assertions from a specific entity_id. :param subject_id: The ID of the subject :param entity_id: The entity ID of the entity_id of the assertion :return: True or False depending on if the assertion is still valid or not. """ item = self._cache.find_one({"subject_id": subject_id, "entity_id": entity_id}) try: return time_util.not_on_or_after(item["timestamp"]) except ToOld: return False
def _get_info(self, item, check_not_on_or_after=True): """ Get session information about a subject gotten from a specified IdP/AA. :param item: Information stored :return: The session information as a dictionary """ timestamp = item["timestamp"] if check_not_on_or_after and not time_util.not_on_or_after(timestamp): raise ToOld() try: return item["info"] except KeyError: return None
def get_info(self, item, check_not_on_or_after=True): """ Get session information about a subject gotten from a specified IdP/AA. :param item: Information stored :return: The session information as a dictionary """ try: (timestamp, info) = item except ValueError: raise ToOld() if check_not_on_or_after and not time_util.not_on_or_after(timestamp): raise ToOld() return info or None
def active(self, subject_id, entity_id): """ Returns the status of assertions from a specific entity_id. :param subject_id: The ID of the subject :param entity_id: The entity ID of the entity_id of the assertion :return: True or False depending on if the assertion is still valid or not. """ try: (timestamp, info) = self._db[subject_id][entity_id] except KeyError: return False if not info: return False else: return time_util.not_on_or_after(timestamp)
def active(self, name_id, entity_id): """ Returns the status of assertions from a specific entity_id. :param name_id: The ID of the subject :param entity_id: The entity ID of the entity_id of the assertion :return: True or False depending on if the assertion is still valid or not. """ try: cni = code(name_id) (timestamp, info) = self._db[cni][entity_id] except KeyError: return False if not info: return False else: return time_util.not_on_or_after(timestamp)
def active(self, subject_id, entity_id): """ Returns the status of assertions from a specific entity_id. :param subject_id: The ID of the subject :param entity_id: The entity ID of the entity_id of the assertion :return: True or False depending on if the assertion is still valid or not. """ try: (timestamp, info) = self._cache.get(_key(subject_id, entity_id)) except ValueError: return False except TypeError: return False # if not info: # return False try: return time_util.not_on_or_after(timestamp) except TooOld: return False
def active(self, subject_id, entity_id): """ Returns the status of assertions from a specific entity_id. :param subject_id: The ID of the subject :param entity_id: The entity ID of the entity_id of the assertion :return: True or False depending on if the assertion is still valid or not. """ try: (timestamp, info) = self._cache.get(_key(subject_id, entity_id)) except ValueError: return False except TypeError: return False # if not info: # return False try: return time_util.not_on_or_after(timestamp) except ToOld: return False
def do_logout( self, name_id, entity_ids, reason, expire, sign=None, expected_binding=None, sign_alg=None, digest_alg=None, **kwargs, ): """ :param name_id: Identifier of the Subject (a NameID instance) :param entity_ids: List of entity ids for the IdPs that have provided information concerning the subject :param reason: The reason for doing the logout :param expire: Try to logout before this time. :param sign: Whether to sign the request or not :param expected_binding: Specify the expected binding then not try it all :param kwargs: Extra key word arguments. :return: """ # check time if not not_on_or_after(expire): # I've run out of time # Do the local logout anyway self.local_logout(name_id) return 0, "504 Gateway Timeout", [], [] not_done = entity_ids[:] responses = {} bindings_slo_preferred = self.config.preferred_binding[ "single_logout_service"] for entity_id in entity_ids: logger.debug("Logout from '%s'", entity_id) bindings_slo_supported = self.metadata.single_logout_service( entity_id=entity_id, typ="idpsso") bindings_slo_preferred_and_supported = ( binding for binding in bindings_slo_preferred if binding in bindings_slo_supported) bindings_slo_choices = filter(lambda x: x, ( expected_binding, *bindings_slo_preferred_and_supported, *bindings_slo_supported, )) binding = next(bindings_slo_choices, None) if not binding: logger.info({ "message": "Entity does not support SLO", "entity": entity_id, }) continue service_info = bindings_slo_supported[binding] service_location = next(locations(service_info), None) if not service_location: logger.info({ "message": "Entity SLO service does not have a location", "entity": entity_id, "service_location": service_location, }) continue session_info = self.users.get_info_from(name_id, entity_id, False) session_index = session_info.get('session_index') session_indexes = [session_index] if session_index else None sign = sign if sign is not None else self.logout_requests_signed sign_post = sign and (binding == BINDING_HTTP_POST or binding == BINDING_SOAP) sign_redirect = sign and binding == BINDING_HTTP_REDIRECT log_report = { "message": "Invoking SLO on entity", "entity": entity_id, "binding": binding, "location": service_location, "session_indexes": session_indexes, "sign": sign, } logger.info(log_report) req_id, request = self.create_logout_request( service_location, entity_id, name_id=name_id, reason=reason, expire=expire, session_indexes=session_indexes, sign=sign_post, sign_alg=sign_alg, digest_alg=digest_alg, ) relay_state = self._relay_state(req_id) http_info = self.apply_binding( binding, str(request), service_location, relay_state, sign=sign_redirect, sigalg=sign_alg, ) if binding == BINDING_SOAP: response = self.send(**http_info) if response and response.status_code == 200: not_done.remove(entity_id) response_text = response.text log_report_response = { **log_report, "message": "Response from SLO service", "response_text": response_text, } logger.debug(log_report_response) res = self.parse_logout_request_response( response_text, binding) responses[entity_id] = res else: log_report_response = { **log_report, "message": "Bad status_code response from SLO service", "status_code": (response and response.status_code), } logger.info(log_report_response) else: self.state[req_id] = { "entity_id": entity_id, "operation": "SLO", "entity_ids": entity_ids, "name_id": code(name_id), "reason": reason, "not_on_or_after": expire, "sign": sign, } responses[entity_id] = (binding, http_info) not_done.remove(entity_id) if not_done: # upstream should try later raise LogoutError("%s" % (entity_ids, )) return responses
def test_not_on_or_after(): current_year = datetime.datetime.today().year assert not_on_or_after("%d-01-01T00:00:00Z" % (current_year + 1)) == True assert not_on_or_after("%d-01-01T00:00:00Z" % (current_year - 1)) == False
def do_logout(self, name_id, entity_ids, reason, expire, sign=None, expected_binding=None, **kwargs): """ :param name_id: Identifier of the Subject (a NameID instance) :param entity_ids: List of entity ids for the IdPs that have provided information concerning the subject :param reason: The reason for doing the logout :param expire: Try to logout before this time. :param sign: Whether to sign the request or not :param expected_binding: Specify the expected binding then not try it all :param kwargs: Extra key word arguments. :return: """ # check time if not not_on_or_after(expire): # I've run out of time # Do the local logout anyway self.local_logout(name_id) return 0, "504 Gateway Timeout", [], [] not_done = entity_ids[:] responses = {} for entity_id in entity_ids: logger.debug("Logout from '%s'", entity_id) # for all where I can use the SOAP binding, do those first for binding in [BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT]: if expected_binding and binding != expected_binding: continue try: srvs = self.metadata.single_logout_service(entity_id, binding, "idpsso") except: srvs = None if not srvs: logger.debug("No SLO '%s' service", binding) continue destination = destinations(srvs)[0] logger.info("destination to provider: %s", destination) try: session_info = self.users.get_info_from(name_id, entity_id, False) session_indexes = [session_info['session_index']] except KeyError: session_indexes = None req_id, request = self.create_logout_request( destination, entity_id, name_id=name_id, reason=reason, expire=expire, session_indexes=session_indexes) # to_sign = [] if binding.startswith("http://"): sign = True if sign is None: sign = self.logout_requests_signed sigalg = None key = None if sign: if binding == BINDING_HTTP_REDIRECT: sigalg = kwargs.get("sigalg", ds.sig_default) key = kwargs.get("key", self.signkey) srequest = str(request) else: srequest = self.sign(request) else: srequest = str(request) relay_state = self._relay_state(req_id) http_info = self.apply_binding(binding, srequest, destination, relay_state, sigalg=sigalg, key=key) if binding == BINDING_SOAP: response = self.send(**http_info) if response and response.status_code == 200: not_done.remove(entity_id) response = response.text logger.info("Response: %s", response) res = self.parse_logout_request_response(response, binding) responses[entity_id] = res else: logger.info("NOT OK response from %s", destination) else: self.state[req_id] = {"entity_id": entity_id, "operation": "SLO", "entity_ids": entity_ids, "name_id": code(name_id), "reason": reason, "not_on_of_after": expire, "sign": sign} responses[entity_id] = (binding, http_info) not_done.remove(entity_id) # only try one binding break if not_done: # upstream should try later raise LogoutError("%s" % (entity_ids,)) return responses
def _logout(self, subject_id, entity_ids, reason, expire, sign=None, return_to="/"): # check time if not not_on_or_after(expire): # I've run out of time # Do the local logout anyway self.local_logout(subject_id) return 0, "504 Gateway Timeout", [], [] # for all where I can use the SOAP binding, do those first not_done = entity_ids[:] response = False for entity_id in entity_ids: response = False for binding in [ BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT ]: destinations = self.config.single_logout_services( entity_id, binding) if not destinations: continue destination = destinations[0] logger.info("destination to provider: %s" % destination) request = self.construct_logout_request( subject_id, destination, entity_id, reason, expire) to_sign = [] #if sign and binding != BINDING_HTTP_REDIRECT: if sign is None: sign = self.logout_requests_signed_default if sign: request.signature = pre_signature_part( request.id, self.sec.my_cert, 1) to_sign = [(class_name(request), request.id)] logger.info("REQUEST: %s" % request) request = signed_instance_factory(request, self.sec, to_sign) if binding == BINDING_SOAP: response = send_using_soap(request, destination, self.config.key_file, self.config.cert_file, ca_certs=self.config.ca_certs) if response: logger.info("Verifying response") response = self.logout_response(response) if response: not_done.remove(entity_id) logger.info("OK response from %s" % destination) else: logger.info("NOT OK response from %s" % destination) else: session_id = request.id rstate = self._relay_state(session_id) self.state[session_id] = { "entity_id": entity_id, "operation": "SLO", "entity_ids": entity_ids, "subject_id": subject_id, "reason": reason, "not_on_of_after": expire, "sign": sign, "return_to": return_to } if binding == BINDING_HTTP_POST: (head, body) = http_post_message(request, destination, rstate) code = "200 OK" else: (head, body) = http_redirect_message(request, destination, rstate) code = "302 Found" return session_id, code, head, body if not_done: # upstream should try later raise LogoutError("%s" % (entity_ids, )) return 0, "", [], response
def _logout(self, subject_id, entity_ids, reason, expire, sign=None, log=None, return_to="/"): # check time if not not_on_or_after(expire): # I've run out of time # Do the local logout anyway self.local_logout(subject_id) return 0, "504 Gateway Timeout", [], [] # for all where I can use the SOAP binding, do those first not_done = entity_ids[:] response = False if log is None: log = self.logger for entity_id in entity_ids: response = False for binding in [BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT]: destinations = self.config.single_logout_services(entity_id, binding) if not destinations: continue destination = destinations[0] if log: log.info("destination to provider: %s" % destination) request = self.construct_logout_request(subject_id, destination, entity_id, reason, expire) to_sign = [] # if sign and binding != BINDING_HTTP_REDIRECT: if sign is None: sign = self.logout_requests_signed_default if sign: request.signature = pre_signature_part(request.id, self.sec.my_cert, 1) to_sign = [(class_name(request), request.id)] if log: log.info("REQUEST: %s" % request) request = signed_instance_factory(request, self.sec, to_sign) if binding == BINDING_SOAP: response = send_using_soap( request, destination, self.config.key_file, self.config.cert_file, log=log, ca_certs=self.config.ca_certs, ) if response: if log: log.info("Verifying response") response = self.logout_response(response, log) if response: not_done.remove(entity_id) if log: log.info("OK response from %s" % destination) else: if log: log.info("NOT OK response from %s" % destination) else: session_id = request.id rstate = self._relay_state(session_id) self.state[session_id] = { "entity_id": entity_id, "operation": "SLO", "entity_ids": entity_ids, "subject_id": subject_id, "reason": reason, "not_on_of_after": expire, "sign": sign, "return_to": return_to, } if binding == BINDING_HTTP_POST: (head, body) = http_post_message(request, destination, rstate) code = "200 OK" else: (head, body) = http_redirect_message(request, destination, rstate) code = "302 Found" return session_id, code, head, body if not_done: # upstream should try later raise LogoutError("%s" % (entity_ids,)) return 0, "", [], response
def do_logout(self, name_id, entity_ids, reason, expire, sign=None, expected_binding=None): """ :param name_id: Identifier of the Subject (a NameID instance) :param entity_ids: List of entity ids for the IdPs that have provided information concerning the subject :param reason: The reason for doing the logout :param expire: Try to logout before this time. :param sign: Whether to sign the request or not :param expected_binding: Specify the expected binding then not try it all :return: """ # check time if not not_on_or_after(expire): # I've run out of time # Do the local logout anyway self.local_logout(name_id) return 0, "504 Gateway Timeout", [], [] not_done = entity_ids[:] responses = {} for entity_id in entity_ids: logger.debug("Logout from '%s'" % entity_id) # for all where I can use the SOAP binding, do those first for binding in [ BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT ]: if expected_binding and binding != expected_binding: continue try: srvs = self.metadata.single_logout_service( entity_id, binding, "idpsso") except: srvs = None if not srvs: logger.debug("No SLO '%s' service" % binding) continue destination = destinations(srvs)[0] logger.info("destination to provider: %s" % destination) req_id, request = self.create_logout_request(destination, entity_id, name_id=name_id, reason=reason, expire=expire) #to_sign = [] if binding.startswith("http://"): sign = True if sign is None: sign = self.logout_requests_signed if sign: srequest = self.sign(request) else: srequest = "%s" % request relay_state = self._relay_state(req_id) http_info = self.apply_binding(binding, srequest, destination, relay_state) if binding == BINDING_SOAP: response = self.send(**http_info) if response and response.status_code == 200: not_done.remove(entity_id) response = response.text logger.info("Response: %s" % response) res = self.parse_logout_request_response(response) responses[entity_id] = res else: logger.info("NOT OK response from %s" % destination) else: self.state[req_id] = { "entity_id": entity_id, "operation": "SLO", "entity_ids": entity_ids, "name_id": code(name_id), "reason": reason, "not_on_of_after": expire, "sign": sign } responses[entity_id] = (binding, http_info) not_done.remove(entity_id) # only try one binding break if not_done: # upstream should try later raise LogoutError("%s" % (entity_ids, )) return responses
def do_logout(self, name_id, entity_ids, reason, expire, sign=None): """ :param name_id: Identifier of the Subject a NameID instance :param entity_ids: List of entity ids for the IdPs that have provided information concerning the subject :param reason: The reason for doing the logout :param expire: Try to logout before this time. :param sign: Whether to sign the request or not :return: """ # check time if not not_on_or_after(expire): # I've run out of time # Do the local logout anyway self.local_logout(name_id) return 0, "504 Gateway Timeout", [], [] not_done = entity_ids[:] responses = {} for entity_id in entity_ids: logger.debug("Logout from '%s'" % entity_id) # for all where I can use the SOAP binding, do those first for binding in [BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT]: try: srvs = self.metadata.single_logout_service(entity_id, binding, "idpsso") except: srvs = None if not srvs: logger.debug("No SLO '%s' service" % binding) continue destination = destinations(srvs)[0] logger.info("destination to provider: %s" % destination) request = self.create_logout_request(destination, entity_id, name_id=name_id, reason=reason, expire=expire) #to_sign = [] if binding.startswith("http://"): sign = True if sign is None: sign = self.logout_requests_signed if sign: srequest = self.sign(request) else: srequest = "%s" % request relay_state = self._relay_state(request.id) http_info = self.apply_binding(binding, srequest, destination, relay_state) if binding == BINDING_SOAP: response = self.send(**http_info) if response and response.status_code == 200: not_done.remove(entity_id) response = response.text logger.info("Response: %s" % response) res = self.parse_logout_request_response(response) responses[entity_id] = res else: logger.info("NOT OK response from %s" % destination) else: self.state[request.id] = {"entity_id": entity_id, "operation": "SLO", "entity_ids": entity_ids, "name_id": name_id, "reason": reason, "not_on_of_after": expire, "sign": sign} responses[entity_id] = (binding, http_info) not_done.remove(entity_id) # only try one binding break if not_done: # upstream should try later raise LogoutError("%s" % (entity_ids,)) return responses
def do_logout( self, name_id, entity_ids, reason, expire, sign=None, expected_binding=None, sign_alg=None, digest_alg=None, **kwargs, ): """ :param name_id: Identifier of the Subject (a NameID instance) :param entity_ids: List of entity ids for the IdPs that have provided information concerning the subject :param reason: The reason for doing the logout :param expire: Try to logout before this time. :param sign: Whether to sign the request or not :param expected_binding: Specify the expected binding then not try it all :param kwargs: Extra key word arguments. :return: """ # check time if not not_on_or_after(expire): # I've run out of time # Do the local logout anyway self.local_logout(name_id) return 0, "504 Gateway Timeout", [], [] not_done = entity_ids[:] responses = {} for entity_id in entity_ids: logger.debug("Logout from '%s'", entity_id) # for all where I can use the SOAP binding, do those first for binding in [BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT]: if expected_binding and binding != expected_binding: continue try: srvs = self.metadata.single_logout_service( entity_id, binding, "idpsso" ) except: srvs = None if not srvs: logger.debug("No SLO '%s' service", binding) continue destination = next(locations(srvs), None) logger.info("destination to provider: %s", destination) try: session_info = self.users.get_info_from( name_id, entity_id, False ) session_indexes = [session_info['session_index']] except KeyError: session_indexes = None sign_post = False if binding == BINDING_HTTP_REDIRECT else sign sign_redirect = False if binding == BINDING_HTTP_POST and sign else sign req_id, request = self.create_logout_request( destination, entity_id, name_id=name_id, reason=reason, expire=expire, session_indexes=session_indexes, sign=sign_post, sign_alg=sign_alg, digest_alg=digest_alg, ) relay_state = self._relay_state(req_id) http_info = self.apply_binding( binding, str(request), destination, relay_state, sign=sign_redirect, sigalg=sign_alg, ) if binding == BINDING_SOAP: response = self.send(**http_info) if response and response.status_code == 200: not_done.remove(entity_id) response = response.text logger.info("Response: %s", response) res = self.parse_logout_request_response(response, binding) responses[entity_id] = res else: logger.info("NOT OK response from %s", destination) else: self.state[req_id] = { "entity_id": entity_id, "operation": "SLO", "entity_ids": entity_ids, "name_id": code(name_id), "reason": reason, "not_on_or_after": expire, "sign": sign, } responses[entity_id] = (binding, http_info) not_done.remove(entity_id) # only try one binding break if not_done: # upstream should try later raise LogoutError("%s" % (entity_ids,)) return responses
def do_logout(self, subject_id, entity_ids, reason, expire, sign=None): """ :param subject_id: Identifier of the Subject :param entity_ids: List of entity ids for the IdPs that have provided information concerning the subject :param reason: The reason for doing the logout :param expire: Try to logout before this time. :param sign: Whether to sign the request or not :return: """ # check time if not not_on_or_after(expire): # I've run out of time # Do the local logout anyway self.local_logout(subject_id) return 0, "504 Gateway Timeout", [], [] # for all where I can use the SOAP binding, do those first not_done = entity_ids[:] responses = {} for entity_id in entity_ids: response = False for binding in [#BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT]: srvs = self.metadata.single_logout_service(entity_id, "idpsso", binding=binding) if not srvs: continue destination = destinations(srvs)[0] logger.info("destination to provider: %s" % destination) request = self.create_logout_request(destination, entity_id, subject_id, reason=reason, expire=expire) to_sign = [] if binding.startswith("http://"): sign = True if sign is None: sign = self.logout_requests_signed_default if sign: request.signature = pre_signature_part(request.id, self.sec.my_cert, 1) to_sign = [(class_name(request), request.id)] logger.info("REQUEST: %s" % request) srequest = signed_instance_factory(request, self.sec, to_sign) if binding == BINDING_SOAP: response = self.send_using_soap(srequest, destination) if response: logger.info("Verifying response") response = self.logout_request_response(response) if response: not_done.remove(entity_id) logger.info("OK response from %s" % destination) responses[entity_id] = logout_response_from_string(response) else: logger.info("NOT OK response from %s" % destination) else: session_id = request.id rstate = self._relay_state(session_id) self.state[session_id] = {"entity_id": entity_id, "operation": "SLO", "entity_ids": entity_ids, "subject_id": subject_id, "reason": reason, "not_on_of_after": expire, "sign": sign} if binding == BINDING_HTTP_POST: response = self.use_http_form_post(srequest, destination, rstate) else: response = self.use_http_get(srequest, destination, rstate) responses[entity_id] = response not_done.remove(entity_id) # only try one binding break if not_done: # upstream should try later raise LogoutError("%s" % (entity_ids,)) return responses