def _authn_statement(self, authn_class=None, authn_auth=None, authn_decl=None, authn_decl_ref=None): """ Construct the AuthnStatement :param authn_class: Authentication Context Class reference :param authn_auth: Authenticating Authority :param authn_decl: Authentication Context Declaration :param authn_decl_ref: Authentication Context Declaration reference :return: An AuthnContext instance """ if authn_class: return factory(saml.AuthnStatement, authn_instant=instant(), session_index=sid(), authn_context=self._authn_context_class_ref( authn_class, authn_auth)) elif authn_decl: return factory(saml.AuthnStatement, authn_instant=instant(), session_index=sid(), authn_context=self._authn_context_decl( authn_decl, authn_auth)) elif authn_decl_ref: return factory(saml.AuthnStatement, authn_instant=instant(), session_index=sid(), authn_context=self._authn_context_decl_ref( authn_decl_ref, authn_auth)) else: return factory(saml.AuthnStatement, authn_instant=instant(), session_index=sid())
def _authn_statement(self, authn_class=None, authn_auth=None, authn_decl=None, authn_decl_ref=None): """ Construct the AuthnStatement :param authn_class: Authentication Context Class reference :param authn_auth: Authenticating Authority :param authn_decl: Authentication Context Declaration :param authn_decl_ref: Authentication Context Declaration reference :return: An AuthnContext instance """ if authn_class: return factory( saml.AuthnStatement, authn_instant=instant(), session_index=sid(), authn_context=self._authn_context_class_ref( authn_class, authn_auth)) elif authn_decl: return factory( saml.AuthnStatement, authn_instant=instant(), session_index=sid(), authn_context=self._authn_context_decl(authn_decl, authn_auth)) elif authn_decl_ref: return factory( saml.AuthnStatement, authn_instant=instant(), session_index=sid(), authn_context=self._authn_context_decl_ref(authn_decl_ref, authn_auth)) else: return factory( saml.AuthnStatement, authn_instant=instant(), session_index=sid())
def authn_statement(authn_class=None, authn_auth=None, authn_decl=None, authn_decl_ref=None, authn_instant="", subject_locality="", session_not_on_or_after=None): """ Construct the AuthnStatement :param authn_class: Authentication Context Class reference :param authn_auth: Authenticating Authority :param authn_decl: Authentication Context Declaration :param authn_decl_ref: Authentication Context Declaration reference :param authn_instant: When the Authentication was performed. Assumed to be seconds since the Epoch. :param subject_locality: Specifies the DNS domain name and IP address for the system from which the assertion subject was apparently authenticated. :return: An AuthnContext instance """ if authn_instant: _instant = instant(time_stamp=authn_instant) else: _instant = instant() if authn_class: res = factory(saml.AuthnStatement, authn_instant=_instant, session_index=sid(), session_not_on_or_after=session_not_on_or_after, authn_context=_authn_context_class_ref( authn_class, authn_auth)) elif authn_decl: res = factory(saml.AuthnStatement, authn_instant=_instant, session_index=sid(), session_not_on_or_after=session_not_on_or_after, authn_context=_authn_context_decl( authn_decl, authn_auth)) elif authn_decl_ref: res = factory(saml.AuthnStatement, authn_instant=_instant, session_index=sid(), session_not_on_or_after=session_not_on_or_after, authn_context=_authn_context_decl_ref( authn_decl_ref, authn_auth)) else: res = factory(saml.AuthnStatement, authn_instant=_instant, session_index=sid(), session_not_on_or_after=session_not_on_or_after) if subject_locality: res.subject_locality = saml.SubjectLocality(text=subject_locality) return res
def authn_statement(authn_class=None, authn_auth=None, authn_decl=None, authn_decl_ref=None, authn_instant="", subject_locality="", session_not_on_or_after=None): """ Construct the AuthnStatement :param authn_class: Authentication Context Class reference :param authn_auth: Authenticating Authority :param authn_decl: Authentication Context Declaration :param authn_decl_ref: Authentication Context Declaration reference :param authn_instant: When the Authentication was performed. Assumed to be seconds since the Epoch. :param subject_locality: Specifies the DNS domain name and IP address for the system from which the assertion subject was apparently authenticated. :return: An AuthnContext instance """ if authn_instant: _instant = instant(time_stamp=authn_instant) else: _instant = instant() if authn_class: res = factory( saml.AuthnStatement, authn_instant=_instant, session_index=sid(), session_not_on_or_after=session_not_on_or_after, authn_context=_authn_context_class_ref( authn_class, authn_auth)) elif authn_decl: res = factory( saml.AuthnStatement, authn_instant=_instant, session_index=sid(), session_not_on_or_after=session_not_on_or_after, authn_context=_authn_context_decl(authn_decl, authn_auth)) elif authn_decl_ref: res = factory( saml.AuthnStatement, authn_instant=_instant, session_index=sid(), session_not_on_or_after=session_not_on_or_after, authn_context=_authn_context_decl_ref(authn_decl_ref, authn_auth)) else: res = factory( saml.AuthnStatement, authn_instant=_instant, session_index=sid(), session_not_on_or_after=session_not_on_or_after) if subject_locality: res.subject_locality = saml.SubjectLocality(text=subject_locality) return res
def add_certificate_to_cache(self, certificate_str): _sid = sid() while _sid in self.certificate_cache(): _sid = sid() cache = self.certificate_cache() cache[_sid] = { "timeout": datetime.datetime.now() + datetime.timedelta(minutes=self.sp_conf.CERT_TIMEOUT), "cert": base64.b64encode(certificate_str) } self.sphandlercache[self.certificate_cache_name] = cache return _sid
def entities_descriptor(eds, valid_for, name, ident, sign, secc): entities = md.EntitiesDescriptor(entity_descriptor=eds) if valid_for: entities.valid_until = in_a_while(hours=valid_for) if name: entities.name = name if ident: entities.id = ident if sign: if not ident: ident = sid() if not secc.key_file: raise Exception("If you want to do signing you should define " + "a key to sign with") if not secc.my_cert: raise Exception("If you want to do signing you should define " + "where your public key are") entities.signature = pre_signature_part(ident, secc.my_cert, 1) entities.id = ident xmldoc = secc.sign_statement("%s" % entities, class_name(entities)) entities = md.entities_descriptor_from_string(xmldoc) return entities
def persistent(self, entity_id, subject_id): """ Keeps the link between a permanent identifier and a temporary/pseudo-temporary identifier for a subject The store supports look-up both ways: from a permanent local identifier to a identifier used talking to a SP and from an identifier given back by an SP to the local permanent. :param entity_id: SP entity ID or VO entity ID :param subject_id: The local permanent identifier of the subject :return: An arbitrary identifier for the subject unique to the service/group of services/VO with a given entity_id """ try: return self._get_remote("persistent", entity_id, subject_id) except KeyError: temp_id = "xyz" while True: temp_id = sid() try: self._get_local("persistent", entity_id, temp_id) except KeyError: break self._store("persistent", entity_id, subject_id, temp_id) self.map.sync() return temp_id
def authz_decision_query(self, entityid, action, evidence=None, resource=None, subject=None, binding=saml2.BINDING_HTTP_REDIRECT, sign=None): """ Creates an authz decision query. :param entityid: The entity ID of the IdP to send the request to :param action: The action you want to perform (has to be at least one) :param evidence: Why you should be able to perform the action :param resource: The resource you want to perform the action on :param subject: Who wants to do the thing :param binding: Which binding to use for sending the request :param sign: Whether the request should be signed or not. :return: AuthzDecisionQuery instance """ spentityid = self._issuer() service_url = self.service_url() my_name = self._my_name() logger.info("spentityid: %s\nservice_url: %s\nmy_name: %s" % ( spentityid, service_url, my_name)) # authen_req = self.authn_request(session_id, location, # service_url, spentityid, my_name, vorg, # scoping, sign) request = samlp.AuthzDecisionQuery(action, evidence, resource, subject=subject, issuer=spentityid, id=sid(), issue_instant=instant(), version=VERSION, destination=entityid) return request
def test_create_artifact_resolve(): b64art = create_artifact(SP, "aabbccddeeffgghhiijj", 1) artifact = base64.b64decode(b64art) #assert artifact[:2] == '\x00\x04' #assert int(artifact[2:4]) == 0 # s = sha1(SP) assert artifact[4:24] == s.digest() idp = Server(config_file="idp_all_conf") typecode = artifact[:2] assert typecode == ARTIFACT_TYPECODE destination = idp.artifact2destination(b64art, "spsso") msg = idp.create_artifact_resolve(b64art, destination, sid()) print msg args = idp.use_soap(msg, destination, None, False) sp = Saml2Client(config_file="servera_conf") ar = sp.parse_artifact_resolve(args["data"]) print ar assert ar.artifact.text == b64art
def _handle_discovery_request(self): """Handle SAML Discovery Service request. This method is called internally by the `authenticate` method when multiple acceptable IdPs are detected. Returns: Tuple containing session Id and Flask Response object to return to user containing either HTTP_REDIRECT to configured Discovery Service end point. Raises: AuthException: when unable to find discovery response end point. """ session_id = sid() try: return_url = self._config.getattr('endpoints', 'sp')['discovery_response'][0][0] except KeyError: raise AuthException( "Multiple IdPs configured with no configured Discovery" + \ " response end point.") return_url += "?session_id=%s" % session_id disco_url = Saml2Client.create_discovery_service_request( self.discovery_service_end_point, self._config.entityid, **{'return': return_url}) LOGGER.debug("Redirect to Discovery Service %s", disco_url) return (session_id, make_response('', 302, {'Location': disco_url}))
def _status_response(self, response_class, issuer, status, sign=False, **kwargs): """ Create a StatusResponse. :param response_class: Which subclass of StatusResponse that should be used :param issuer: The issuer of the response message :param status: The return status of the response operation :param sign: Whether the response should be signed or not :param kwargs: Extra arguments to the response class :return: Class instance or string representation of the instance """ mid = sid() for key in ["destination", "binding"]: try: del kwargs[key] except KeyError: pass if not status: status = success_status_factory() response = response_class(issuer=issuer, id=mid, version=VERSION, issue_instant=instant(), status=status, **kwargs) if sign: return self.sign(response, mid) else: return response
def make_logout_response(self, idp_entity_id, request_id, status_code, binding=BINDING_HTTP_REDIRECT): """ Constructs a LogoutResponse :param idp_entity_id: The entityid of the IdP that want to do the logout :param request_id: The Id of the request we are replying to :param status_code: The status code of the response :param binding: The type of binding that will be used for the response :return: A LogoutResponse instance """ destination = self.config.single_logout_services( idp_entity_id, binding)[0] status = samlp.Status(status_code=samlp.StatusCode(value=status_code)) response = samlp.LogoutResponse( id=sid(), version=VERSION, issue_instant=instant(), destination=destination, issuer=self._issuer(), in_response_to=request_id, status=status, ) return response, destination
def entities_descriptor(eds, valid_for, name, ident, sign, secc): entities = md.EntitiesDescriptor(entity_descriptor=eds) if valid_for: entities.valid_until = in_a_while(hours=valid_for) if name: entities.name = name if ident: entities.id = ident if sign: if not ident: ident = sid() if not secc.key_file: raise SAMLError("If you want to do signing you should define " + "a key to sign with") if not secc.my_cert: raise SAMLError("If you want to do signing you should define " + "where your public key are") entities.signature = pre_signature_part(ident, secc.my_cert, 1) entities.id = ident xmldoc = secc.sign_statement("%s" % entities, class_name(entities)) entities = md.entities_descriptor_from_string(xmldoc) else: xmldoc = None return entities, xmldoc
def test_create_artifact_resolve(): b64art = create_artifact(SP, "aabbccddeeffgghhiijj", 1) artifact = base64.b64decode(b64art) #assert artifact[:2] == '\x00\x04' #assert int(artifact[2:4]) == 0 # s = sha1(SP.encode('ascii')) assert artifact[4:24] == s.digest() with closing(Server(config_file="idp_all_conf")) as idp: typecode = artifact[:2] assert typecode == ARTIFACT_TYPECODE destination = idp.artifact2destination(b64art, "spsso") msg_id, msg = idp.create_artifact_resolve(b64art, destination, sid()) print(msg) args = idp.use_soap(msg, destination, None, False) sp = Saml2Client(config_file="servera_conf") ar = sp.parse_artifact_resolve(args["data"]) print(ar) assert ar.artifact.text == b64art
def multiple_signatures(self, statement, to_sign, key=None, key_file=None): """ Sign multiple parts of a statement :param statement: The statement that should be sign, this is XML text :param to_sign: A list of (items, id, id attribute name) tuples that specifies what to sign :param key: A key that should be used for doing the signing :param key_file: A file that contains the key to be used :return: A possibly multiple signed statement """ for (item, id, id_attr) in to_sign: if not id: if not item.id: id = item.id = sid() else: id = item.id if not item.signature: item.signature = pre_signature_part(id, self.cert_file) statement = self.sign_statement_using_xmlsec(statement, class_name(item), key=key, key_file=key_file, nodeid=id, id_attr=id_attr) return statement
def multiple_signatures(self, statement, to_sign, key=None, key_file=None): """ Sign multiple parts of a statement :param statement: The statement that should be sign, this is XML text :param to_sign: A list of (items, id, id attribute name) tuples that specifies what to sign :param key: A key that should be used for doing the signing :param key_file: A file that contains the key to be used :return: A possibly multiple signed statement """ for (item, sid, id_attr) in to_sign: if not sid: if not item.id: sid = item.id = sid() else: sid = item.id if not item.signature: item.signature = pre_signature_part(sid, self.cert_file) statement = self.sign_statement(statement, class_name(item), key=key, key_file=key_file, node_id=sid, id_attr=id_attr) return statement
def authenticate(self, entityid=None, relay_state="", binding=BINDING_HTTP_REDIRECT, log=None, vorg="", scoping=None, sign=None, **kwargs): """ Makes an authentication request. :param entityid: The entity ID of the IdP to send the request to :param relay_state: To where the user should be returned after successfull log in. :param binding: Which binding to use for sending the request :param log: Where to write log messages :param vorg: The entity_id of the virtual organization I'm a member of :param scoping: For which IdPs this query are aimed. :param sign: Whether the request should be signed or not. :return: AuthnRequest response """ destination = self._sso_location(entityid, binding=binding) session_id = sid() _req_str = "%s" % self.authn(destination, session_id, vorg, scoping, log, sign, **kwargs) logger.info("AuthNReq: %s" % _req_str) info = self.apply_binding(binding, _req_str, destination, relay_state) return session_id, info
def construct_logout_request(self, subject_id, destination, issuer_entity_id, reason=None, expire=None): """ Constructs a LogoutRequest :param subject_id: The identifier of the subject :param destination: :param issuer_entity_id: The entity ID of the IdP the request is target at. :param reason: An indication of the reason for the logout, in the form of a URI reference. :param expire: The time at which the request expires, after which the recipient may discard the message. :return: A LogoutRequest instance """ session_id = sid() # create NameID from subject_id name_id = saml.NameID(text=self.users.get_entityid(subject_id, issuer_entity_id, False)) request = samlp.LogoutRequest( id=session_id, version=VERSION, issue_instant=instant(), destination=destination, issuer=self.issuer(), name_id=name_id, ) if reason: request.reason = reason if expire: request.not_on_or_after = expire return request
def make_logout_response(self, idp_entity_id, request_id, status_code, binding=BINDING_HTTP_REDIRECT): """ Constructs a LogoutResponse :param idp_entity_id: The entityid of the IdP that want to do the logout :param request_id: The Id of the request we are replying to :param status_code: The status code of the response :param binding: The type of binding that will be used for the response :return: A LogoutResponse instance """ destination = self.config.single_logout_services(idp_entity_id, binding)[0] status = samlp.Status(status_code=samlp.StatusCode(value=status_code)) response = samlp.LogoutResponse( id=sid(), version=VERSION, issue_instant=instant(), destination=destination, issuer=self.issuer(), in_response_to=request_id, status=status, ) return response, destination
def _handle_discovery_request(self): """Handle SAML Discovery Service request. This method is called internally by the `authenticate` method when multiple acceptable IdPs are detected. Returns: Tuple containing session Id and Flask Response object to return to user containing either HTTP_REDIRECT to configured Discovery Service end point. Raises: AuthException: when unable to find discovery response end point. """ session_id = sid() try: return_url = self._config.getattr( 'endpoints', 'sp')['discovery_response'][0][0] except KeyError: raise AuthException( "Multiple IdPs configured with no configured Discovery" + \ " response end point.") return_url += "?session_id=%s" % session_id disco_url = Saml2Client.create_discovery_service_request( self.discovery_service_end_point, self._config.entityid, **{'return': return_url}) LOGGER.debug("Redirect to Discovery Service %s", disco_url) return (session_id, make_response('', 302, {'Location': disco_url}))
def __init__(self, sp_logger, args): """ Constructor for the SpHandler. :param sp_logger: A logger. """ #Metadata for the SP self.sp_metadata = create_metadata_string(args.spconf + ".py", None, args.valid, args.cert, args.keyfile, args.id_sp, args.name_sp, args.sign) #Log class. (see import logging) self.logger = sp_logger #Configurations for the SP handler. (pyOpSamlProxy.client.sp.conf) self.sp_conf = importlib.import_module( args.spconf) #pyOpSamlProxy.client.sp.conf #Name of the configuration file. See above. self.sp_conf_name = self.sp_conf.WORKING_DIR + args.spconf #SP configuration object. (See project pysaml2; saml2.client.Saml2Client) self.sp = Saml2Client(config_file="%s" % self.sp_conf_name) #Extra arguments for the pyOpSamlProxy.client.sp.util.SSO object. self.args = {} #URL to SAML discovery server. self.args["discosrv"] = self.sp_conf.DISCOSRV #URL to SAML WAYF server. self.args["wayf"] = self.sp_conf.WAYF #URL to op server authorization when the SP have been authenticated. #TODO have to be changed when Saml to Saml is implemented. self.authorization_url = "%s/authorization" % self.sp_conf.ISSUER #Handles the SAML authentication for an op server. self.authnmethod = SPAuthnMethodHandler(None, self.sp_conf.SPVERIFYBASE, self.authorization_url) #Handles SAML authentication for an IdP server. # Setup performed by pyOpSamlProxy.provider.idp.handler.handler. self.sp_authentication = None #Handles the user info response with Saml attributes. self.userinfo = UserInfoSpHandler(self.sp_conf.OPENID2SAMLMAP, self) #The handler for the op server. Must be set after creation #This must be the instance of the class pyOpSamlProxy.provider.op.handler.OpHandler. self.ophandler = None #Contains the user cache for the SpHandler, like collected IdP attributes. #Dictionary where userid is key and value is an instance of the class #pyOpSamlProxy.client.sp.handler.SpHandlerCache self.sphandlercache = self.sp_conf.CACHE self.certificate_cache_name = "CERTIFICATE_CACHE" self.certificate_cookie_name = sid() self.certificate_cookie_seed = sid()
def _authn_statement(self, authn_class=None, authn_auth=None, authn_decl=None): if authn_class: return factory(saml.AuthnStatement, authn_instant=instant(), session_index=sid(), authn_context=self._authn_context_class_ref( authn_class, authn_auth)) elif authn_decl: return factory(saml.AuthnStatement, authn_instant=instant(), session_index=sid(), authn_context=self._authn_context_decl_ref(authn_decl)) else: return factory(saml.AuthnStatement, authn_instant=instant(), session_index=sid())
def _authn_statement(self, authn_class=None, authn_auth=None, authn_decl=None): if authn_class: return factory( saml.AuthnStatement, authn_instant=instant(), session_index=sid(), authn_context=self._authn_context_class_ref(authn_class, authn_auth), ) elif authn_decl: return factory( saml.AuthnStatement, authn_instant=instant(), session_index=sid(), authn_context=self._authn_context_decl_ref(authn_decl), ) else: return factory(saml.AuthnStatement, authn_instant=instant(), session_index=sid())
def sign_entity_descriptor(edesc, ident, secc): if not ident: ident = sid() edesc.signature = pre_signature_part(ident, secc.my_cert, 1) edesc.id = ident xmldoc = secc.sign_statement_using_xmlsec("%s" % edesc, class_name(edesc)) return md.entity_descriptor_from_string(xmldoc)
def sign_entity_descriptor(edesc, ident, secc): if not ident: ident = sid() edesc.signature = pre_signature_part(ident, secc.my_cert, 1) edesc.id = ident xmldoc = secc.sign_statement("%s" % edesc, class_name(edesc)) return md.entity_descriptor_from_string(xmldoc)
def _wayf_redirect(self, cookie): sid_ = sid() self.cache_outstanding_queries[sid_] = self.verification_endpoint return ( -1, SeeOther( headers=[("Location", "%s?%s" % (self.sp_conf.WAYF, sid_)), cookie] ), )
def authentication_request(cls, ecp, idp_entity_id, destination, log=None, sign=False): """ Does a authentication request to an Identity provider. This function uses the SOAP binding other bindings could be used but are not supported right now. :param cls: The SAML2 client instance :param ecp: The ECP client instance :param idp_entity_id: The identifier of the subject :param destination: To whom the query should be sent :param log: Function to use for logging :param sign: Whether the request should be signed or not :return: A Authentication Response """ if log is None: log = cls.logger session_id = sid() acsus = cls.config.endpoint('assertion_consumer_service', saml2.BINDING_PAOS) if not acsus and log: log.error("Couldn't find own PAOS endpoint") acsu = acsus[0] spentityid = cls.config.entityid # create the request request = cls.authn_request(session_id, destination, acsu, spentityid, "", log=LOG(), sign=sign, binding=saml2.BINDING_PAOS, nameid_format=saml.NAMEID_FORMAT_PERSISTENT) try: try: headers = {config.USERNAME_HEADER: ecp.user} except AttributeError: headers = None print >> sys.stderr, "Headers: {0:>s}".format(headers) # send the request and receive the response response = ecp.phase2(request, acsu, idp_entity_id, headers, destination) except Exception, exc: exception_trace("soap", exc, log) if log: log.info("SoapClient exception: %s" % (exc,)) return None
def transient_nameid(self, sp_entity_id, userid): """ Returns a random one-time identifier. One-time means it is kept around as long as the session is active. :param sp_entity_id: A qualifier to bind the created identifier to :param userid: The local persistent identifier for the subject. :return: The created identifier, """ temp_id = sid() while True: try: _ = self._get_local("transient", sp_entity_id, temp_id) temp_id = sid() except KeyError: break self._store("transient", sp_entity_id, userid, temp_id) self.map.sync() return saml.NameID(format=saml.NAMEID_FORMAT_TRANSIENT, sp_name_qualifier=sp_entity_id, text=temp_id)
def message_args(self, message_id=0): if not message_id: message_id = sid(self.seed) return { "id": message_id, "version": VERSION, "issue_instant": instant(), "issuer": self._issuer() }
def __init__(self, sp_logger, args): """ Constructor for the SpHandler. :param sp_logger: A logger. """ #Metadata for the SP self.sp_metadata = create_metadata_string(args.spconf + ".py", None, args.valid, args.cert, args.keyfile, args.id_sp, args.name_sp, args.sign) #Log class. (see import logging) self.logger = sp_logger #Configurations for the SP handler. (pyOpSamlProxy.client.sp.conf) self.sp_conf = importlib.import_module(args.spconf) #pyOpSamlProxy.client.sp.conf #Name of the configuration file. See above. self.sp_conf_name = self.sp_conf.WORKING_DIR + args.spconf #SP configuration object. (See project pysaml2; saml2.client.Saml2Client) self.sp = Saml2Client(config_file="%s" % self.sp_conf_name) #Extra arguments for the pyOpSamlProxy.client.sp.util.SSO object. self.args = {} #URL to SAML discovery server. self.args["discosrv"] = self.sp_conf.DISCOSRV #URL to SAML WAYF server. self.args["wayf"] = self.sp_conf.WAYF #URL to op server authorization when the SP have been authenticated. #TODO have to be changed when Saml to Saml is implemented. self.authorization_url = "%s/authorization" % self.sp_conf.ISSUER #Handles the SAML authentication for an op server. self.authnmethod = SPAuthnMethodHandler(None, self.sp_conf.SPVERIFYBASE, self.authorization_url) #Handles SAML authentication for an IdP server. # Setup performed by pyOpSamlProxy.provider.idp.handler.handler. self.sp_authentication = None #Handles the user info response with Saml attributes. self.userinfo = UserInfoSpHandler(self.sp_conf.OPENID2SAMLMAP, self) #The handler for the op server. Must be set after creation #This must be the instance of the class pyOpSamlProxy.provider.op.handler.OpHandler. self.ophandler = None #Contains the user cache for the SpHandler, like collected IdP attributes. #Dictionary where userid is key and value is an instance of the class #pyOpSamlProxy.client.sp.handler.SpHandlerCache self.sphandlercache = self.sp_conf.CACHE self.certificate_cache_name = "CERTIFICATE_CACHE" self.certificate_cookie_name = sid() self.certificate_cookie_seed = sid()
def sign_entity_descriptor(edesc, valid_for, ident, secc): if valid_for: edesc.valid_until = in_a_while(hours=valid_for) if not ident: ident = sid() edesc.signature = pre_signature_part(ident, secc.my_cert, 1) edesc.id = ident xmldoc = secc.sign_statement_using_xmlsec("%s" % edesc, class_name(edesc)) return md.entity_descriptor_from_string(xmldoc)
def construct_message(self): session_id = sid() # Should be bound to session sp = self.entity url = sp.config.getattr("endpoints", "sp")["discovery_response"][0][0] return_to = "{url}?{query}".format(url=url, query=urlencode(({"sid": session_id}))) redirect_url = sp.create_discovery_service_request( self.req_args["discovery_service_url"], sp.config.entityid, **{"return": return_to} ) logger.debug("Redirect to Discovery Service: %s", redirect_url) self.conv.events.store(EV_REDIRECT_URL, redirect_url, sub="construct_message", sender=self.__class__) return SeeOther(redirect_url)
def disco_srv(environ, start_response): disco_url = environ["PATH_INFO"][4:] sid_ = sid() #SP.cache.outstanding_queries[sid_] = "/userinfo" logger.info("Redirect to Discovery Service function") eid = SP.config.entityid ret = SP.config.getattr("endpoints", "sp")["discovery_response"][0][0] ret += "?sid=%s" % sid_ loc = SP.create_discovery_service_request(disco_url, eid, **{"return": ret}) resp = SeeOther(loc) return resp(environ, start_response)
def attribute_query(cls, subject_id, destination, attribute=None, name_id=None, sp_name_qualifier=None, name_qualifier=None, nameid_format=None, sign=False): """ Does a attribute request to an attribute authority, this is by default done over SOAP. Other bindings could be used but are not supported right now. :param subject_id: The identifier of the subject :param destination: To whom the query should be sent :param attribute: A dictionary of attributes and values that is asked for :param name_id: A NameID instance that describes the entity the information is asked for. :param sp_name_qualifier: The unique identifier of the service provider or affiliation of providers for whom the identifier was generated. :param name_qualifier: The unique identifier of the identity provider that generated the identifier. :param nameid_format: The format of the name ID :param sign: Whether the request should be signed or not :return: The Assertion """ global CLIENT logger = LOG() session_id = sid() if not name_id: args = { "subject_id": subject_id, "sp_name_qualifier": sp_name_qualifier, "format": nameid_format, "name_qualifier": name_qualifier } if not name_qualifier and not sp_name_qualifier: args["sp_name_qualifier"] = cls.config.entityid else: args = {"name_id": name_id} if sign: args["sign_prepare"] = True request = cls.create_attribute_query(destination, attribute=attribute, message_id=session_id, **args) try: args = CLIENT.use_soap(request, destination, sign=sign) response = CLIENT.send(**args) except Exception, exc: exception_trace("SoapClient exception", exc, logger) return None
def attribute_query(self, subject_id, destination, issuer_id=None, attribute=None, sp_name_qualifier=None, name_qualifier=None, nameid_format=None, real_id=None): """ Does a attribute request to an attribute authority, this is by default done over SOAP. Other bindings could be used but not supported right now. :param subject_id: The identifier of the subject :param destination: To whom the query should be sent :param issuer_id: Who is sending this query :param attribute: A dictionary of attributes and values that is asked for :param sp_name_qualifier: The unique identifier of the service provider or affiliation of providers for whom the identifier was generated. :param name_qualifier: The unique identifier of the identity provider that generated the identifier. :param nameid_format: The format of the name ID :param real_id: The identifier which is the key to this entity in the identity database :return: The attributes returned """ session_id = sid() issuer = self._issuer(issuer_id) request = self.create_attribute_query(session_id, subject_id, destination, issuer, attribute, sp_name_qualifier, name_qualifier, nameid_format=nameid_format) logger.info("Request, created: %s" % request) soapclient = SOAPClient(destination, self.config.key_file, self.config.cert_file, ca_certs=self.config.ca_certs) logger.info("SOAP client initiated") try: response = soapclient.send(request) except Exception, exc: logger.info("SoapClient exception: %s" % (exc, )) return None
def _message(self, request_cls, destination=None, message_id=0, consent=None, extensions=None, sign=False, sign_prepare=False, nsprefix=None, **kwargs): """ Some parameters appear in all requests so simplify by doing it in one place :param request_cls: The specific request type :param destination: The recipient :param message_id: A message identifier :param consent: Whether the principal have given her consent :param extensions: Possible extensions :param sign: Whether the request should be signed or not. :param sign_prepare: Whether the signature should be prepared or not. :param kwargs: Key word arguments specific to one request type :return: A tuple containing the request ID and an instance of the request_cls """ if not message_id: message_id = sid() for key, val in self.message_args(message_id).items(): if key not in kwargs: kwargs[key] = val req = request_cls(**kwargs) reqid = req.id if destination: req.destination = destination if consent: req.consent = "true" if extensions: req.extensions = extensions if nsprefix: req.register_prefix(nsprefix) if sign: return reqid, self.sign(req, sign_prepare=sign_prepare) else: logger.info("REQUEST: %s" % req) return reqid, req
def attribute_query(cls, subject_id, destination, issuer_id=None, attribute=None, sp_name_qualifier=None, name_qualifier=None, nameid_format=None, log=None, sign=False): """ Does a attribute request to an attribute authority, this is by default done over SOAP. Other bindings could be used but are not supported right now. :param subject_id: The identifier of the subject :param destination: To whom the query should be sent :param issuer_id: Who is sending this query :param attribute: A dictionary of attributes and values that is asked for :param sp_name_qualifier: The unique identifier of the service provider or affiliation of providers for whom the identifier was generated. :param name_qualifier: The unique identifier of the identity provider that generated the identifier. :param nameid_format: The format of the name ID :param log: Function to use for logging :param sign: Whether the request should be signed or not :return: The Assertion """ if log is None: log = cls.logger session_id = sid() issuer = cls.issuer(issuer_id) if not name_qualifier and not sp_name_qualifier: sp_name_qualifier = cls.config.entityid request = cls.create_attribute_query(session_id, subject_id, destination, issuer, attribute, sp_name_qualifier, name_qualifier, nameid_format=nameid_format) # soapclient = HTTP.send(destination, cls.config.key_file, # cls.config.cert_file) if sign: request.signature= pre_signature_part(request.id, cls.sec.my_cert, 1) try: if sign: response = HTTP.send(request, path=destination, sign=True, sec=cls.sec) else: response = HTTP.send(request, path=destination) except Exception, exc: exception_trace("SoapClient exception", exc, log) return None
def response_factory(sign=False, encrypt=False, **kwargs): response = samlp.Response(id=sid(), version=VERSION, issue_instant=instant()) if sign: response.signature = pre_signature_part(kwargs["id"]) if encrypt: pass for key, val in kwargs.items(): setattr(response, key, val) return response
def create_authn_request(self): try: #sid_ = sid() #self.outstanding_queries[sid_] = came_from idps = self.sp.metadata.with_descriptor("idpsso") if len(idps) == 1: self.entity_id = idps.keys()[0] elif len(idps) > 1: raise Exception("TestSp only supports 1 idp in the metadata!") else: Exception("No IdP metadata found!") _binding, destination = self.sp.pick_binding("single_sign_on_service", self.bindings, "idpsso", entity_id=self.entity_id) self.cert_str, self.cert_key_str = self.generate_cert() cert = { "cert": self.cert_str, "key": self.cert_key_str } spcertenc = SPCertEnc( x509_data=xmldsig.X509Data(x509_certificate=xmldsig.X509Certificate(text=self.cert_str))) extensions = Extensions(extension_elements=[element_to_extension_element(spcertenc)]) try: vorg_name = self.sp.vorg._name except AttributeError: vorg_name = "" if self.sp.authn_requests_signed: self.sid = s_utils.sid() req_id, self.msg_str = self.sp.create_authn_request(destination, vorg=vorg_name, sign=self.sp.authn_requests_signed, message_id=self.sid, extensions=extensions) self.sid = req_id else: req_id, req = self.sp.create_authn_request(destination, vorg=vorg_name, sign=False) self.msg_str = "%s" % req self.sid = req_id if cert is not None: self.outstanding_certs[self.sid] = cert self.rstate = rndstr() self.ht_args = self.sp.apply_binding(_binding, self.msg_str, destination, relay_state=self.rstate) url = self.ht_args["headers"][0][1] except Exception, exc: raise Exception("Failed to construct the AuthnRequest: %s" % exc)
def _message(self, request_cls, destination=None, message_id=0, consent=None, extensions=None, sign=False, sign_prepare=False, nsprefix=None, sign_alg=None, digest_alg=None, **kwargs): """ Some parameters appear in all requests so simplify by doing it in one place :param request_cls: The specific request type :param destination: The recipient :param message_id: A message identifier :param consent: Whether the principal have given her consent :param extensions: Possible extensions :param sign: Whether the request should be signed or not. :param sign_prepare: Whether the signature should be prepared or not. :param kwargs: Key word arguments specific to one request type :return: A tuple containing the request ID and an instance of the request_cls """ if not message_id: message_id = sid() for key, val in self.message_args(message_id).items(): if key not in kwargs: kwargs[key] = val req = request_cls(**kwargs) if destination: req.destination = destination if consent: req.consent = "true" if extensions: req.extensions = extensions if nsprefix: req.register_prefix(nsprefix) if self.msg_cb: req = self.msg_cb(req) reqid = req.id if sign: return reqid, self.sign(req, sign_prepare=sign_prepare, sign_alg=sign_alg, digest_alg=digest_alg) else: logger.info("REQUEST: %s", req) return reqid, req
def attribute_query(self, subject_id, destination, issuer_id=None, attribute=None, sp_name_qualifier=None, name_qualifier=None, nameid_format=None, log=None, real_id=None): """ Does a attribute request to an attribute authority, this is by default done over SOAP. Other bindings could be used but not supported right now. :param subject_id: The identifier of the subject :param destination: To whom the query should be sent :param issuer_id: Who is sending this query :param attribute: A dictionary of attributes and values that is asked for :param sp_name_qualifier: The unique identifier of the service provider or affiliation of providers for whom the identifier was generated. :param name_qualifier: The unique identifier of the identity provider that generated the identifier. :param nameid_format: The format of the name ID :param log: Function to use for logging :param real_id: The identifier which is the key to this entity in the identity database :return: The attributes returned """ if log is None: log = self.logger session_id = sid() issuer = self._issuer(issuer_id) request = self.create_attribute_query(session_id, subject_id, destination, issuer, attribute, sp_name_qualifier, name_qualifier, nameid_format=nameid_format) if log: log.info("Request, created: %s" % request) soapclient = SOAPClient(destination, self.config.key_file, self.config.cert_file, ca_certs=self.config.ca_certs) if log: log.info("SOAP client initiated") try: response = soapclient.send(request) except Exception, exc: if log: log.info("SoapClient exception: %s" % (exc,)) return None
def artifact2message(self, artifact, descriptor): """ :param artifact: The Base64 encoded SAML artifact as sent over the net :param descriptor: The type of entity on the other side :return: A SAML message (request/response) """ destination = self.artifact2destination(artifact, descriptor) if not destination: raise SAMLError("Missing endpoint location") _sid = sid() msg = self.create_artifact_resolve(artifact, destination, _sid) return self.send_using_soap(msg, destination)
def _message(self, request_cls, destination=None, id=0, consent=None, extensions=None, sign=False, **kwargs): """ Some parameters appear in all requests so simplify by doing it in one place :param request_cls: The specific request type :param destination: The recipient :param id: A message identifier :param consent: Whether the principal have given her consent :param extensions: Possible extensions :param kwargs: Key word arguments specific to one request type :return: An instance of the request_cls """ if not id: id = sid(self.seed) req = request_cls(id=id, version=VERSION, issue_instant=instant(), issuer=self._issuer(), **kwargs) if destination: req.destination = destination if consent: req.consent = consent if extensions: req.extensions = extensions if sign: req.signature = pre_signature_part(req.id, self.sec.my_cert, 1) to_sign = [(class_name(req), req.id)] else: to_sign = [] logger.info("REQUEST: %s" % req) return signed_instance_factory(req, self.sec, to_sign)
def sign_entity_descriptor(edesc, ident, secc): """ :param edesc: EntityDescriptor instance :param ident: EntityDescriptor identifier :param secc: Security context :return: Tuple with EntityDescriptor instance and Signed XML document """ if not ident: ident = sid() edesc.signature = pre_signature_part(ident, secc.my_cert, 1) edesc.id = ident xmldoc = secc.sign_statement("%s" % edesc, class_name(edesc)) edesc = md.entity_descriptor_from_string(xmldoc) return edesc, xmldoc
def authenticate( self, entityid=None, relay_state="", binding=saml2.BINDING_HTTP_REDIRECT, log=None, vorg="", scoping=None, sign=None, ): """ Makes an authentication request. :param entityid: The entity ID of the IdP to send the request to :param relay_state: To where the user should be returned after successfull log in. :param binding: Which binding to use for sending the request :param log: Where to write log messages :param vorg: The entity_id of the virtual organization I'm a member of :param scoping: For which IdPs this query are aimed. :param sign: Whether the request should be signed or not. :return: AuthnRequest response """ location = self._sso_location(entityid) session_id = sid() _req_str = "%s" % self.authn(location, session_id, vorg, scoping, log, sign) if log: log.info("AuthNReq: %s" % _req_str) if binding == saml2.BINDING_HTTP_POST: # No valid ticket; Send a form to the client # THIS IS NOT TO BE USED RIGHT NOW if log: log.info("HTTP POST") (head, response) = http_post_message(_req_str, location, relay_state) elif binding == saml2.BINDING_HTTP_REDIRECT: if log: log.info("HTTP REDIRECT") (head, _body) = http_redirect_message(_req_str, location, relay_state) response = head[0] else: raise Exception("Unkown binding type: %s" % binding) return session_id, response
def create_logout_response(self, request, binding, status=None, sign=False, issuer=None): """ Create a LogoutResponse. What is returned depends on which binding is used. :param request: The request this is a response to :param binding: Which binding the request came in over :param status: The return status of the response operation :param issuer: The issuer of the message :return: A logout message. """ mid = sid() if not status: status = success_status_factory() # response and packaging differs depending on binding response = "" if binding in [BINDING_SOAP, BINDING_HTTP_POST]: response = logoutresponse_factory(sign=sign, id = mid, in_response_to = request.id, status = status) elif binding == BINDING_HTTP_REDIRECT: sp_entity_id = request.issuer.text.strip() srvs = self.metadata.single_logout_service(sp_entity_id, "spsso") if not srvs: raise Exception("Nowhere to send the response") destination = destinations(srvs)[0] _issuer = self.issuer(issuer) response = logoutresponse_factory(sign=sign, id = mid, in_response_to = request.id, status = status, issuer = _issuer, destination = destination, sp_entity_id = sp_entity_id, instant=instant()) if sign: to_sign = [(class_name(response), mid)] response = signed_instance_factory(response, self.sec, to_sign) logger.info("Response: %s" % (response,)) return response
def authz_decision_query(self, entityid, action, evidence=None, resource=None, subject=None, binding=saml2.BINDING_HTTP_REDIRECT, sign=None): """ Creates an authz decision query. :param entityid: The entity ID of the IdP to send the request to :param action: The action you want to perform (has to be at least one) :param evidence: Why you should be able to perform the action :param resource: The resource you want to perform the action on :param subject: Who wants to do the thing :param binding: Which binding to use for sending the request :param sign: Whether the request should be signed or not. :return: AuthzDecisionQuery instance """ spentityid = self._issuer() service_url = self.service_url() my_name = self._my_name() logger.info("spentityid: %s\nservice_url: %s\nmy_name: %s" % (spentityid, service_url, my_name)) # authen_req = self.authn_request(session_id, location, # service_url, spentityid, my_name, vorg, # scoping, sign) request = samlp.AuthzDecisionQuery(action, evidence, resource, subject=subject, issuer=spentityid, id=sid(), issue_instant=instant(), version=VERSION, destination=entityid) return request
def authenticate(self, entityid=None, relay_state="", binding=saml2.BINDING_HTTP_REDIRECT, vorg="", scoping=None, sign=None): """ Makes an authentication request. :param entityid: The entity ID of the IdP to send the request to :param relay_state: To where the user should be returned after successfull log in. :param binding: Which binding to use for sending the request :param vorg: The entity_id of the virtual organization I'm a member of :param scoping: For which IdPs this query are aimed. :param sign: Whether the request should be signed or not. :return: AuthnRequest response """ location = self._sso_location(entityid, binding) session_id = sid() _req_str = "%s" % self.authn(location, session_id, vorg, scoping, sign) logger.info("AuthNReq: %s" % _req_str) if binding == saml2.BINDING_HTTP_POST: # No valid ticket; Send a form to the client # THIS IS NOT TO BE USED RIGHT NOW logger.info("HTTP POST") (head, response) = http_post_message(_req_str, location, relay_state) elif binding == saml2.BINDING_HTTP_REDIRECT: logger.info("HTTP REDIRECT") (head, _body) = http_redirect_message(_req_str, location, relay_state) response = head[0] else: raise Exception("Unkown binding type: %s" % binding) return session_id, response
def make_logout_response(self, idp_entity_id, request_id, status_code, binding=BINDING_HTTP_REDIRECT): """ XXX There were issues with an explicit closing tag on StatusCode. Check wether we still need this. XXX Constructs a LogoutResponse :param idp_entity_id: The entityid of the IdP that want to do the logout :param request_id: The Id of the request we are replying to :param status_code: The status code of the response :param binding: The type of binding that will be used for the response :return: A LogoutResponse instance """ srvs = self.metadata.single_logout_service(idp_entity_id, binding, "idpsso") destination = destinations(srvs)[0] logger.info("destination to provider: %s" % destination) status = samlp.Status( status_code=samlp.StatusCode(value=status_code, text='\n'), status_message=samlp.StatusMessage(text='logout success')) response = samlp.LogoutResponse( id=sid(), version=VERSION, issue_instant=instant(), destination=destination, issuer=saml.Issuer(text=self.config.entityid, format=saml.NAMEID_FORMAT_ENTITY), in_response_to=request_id, status=status, ) return response, destination
def construct_logout_request(self, subject_id, destination, issuer_entity_id, reason=None, expire=None): """ Constructs a LogoutRequest :param subject_id: The identifier of the subject :param destination: :param issuer_entity_id: The entity ID of the IdP the request is target at. :param reason: An indication of the reason for the logout, in the form of a URI reference. :param expire: The time at which the request expires, after which the recipient may discard the message. :return: A LogoutRequest instance """ session_id = sid() # create NameID from subject_id name_id = saml.NameID( text=self.users.get_entityid(subject_id, issuer_entity_id, False)) request = samlp.LogoutRequest(id=session_id, version=VERSION, issue_instant=instant(), destination=destination, issuer=self._issuer(), name_id=name_id) if reason: request.reason = reason if expire: request.not_on_or_after = expire return request