def echo_attributes(request, config_loader_path=None, template='djangosaml2/echo_attributes.html'): """Example view that echo the SAML attributes of an user""" state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) subject_id = _get_subject_id(request.session) identity = client.users.get_identity(subject_id, check_not_on_or_after=False) return render(request, template, {'attributes': identity[0]})
def logout_service(request, config_loader_path=None, next_page=None): """SAML Logout Response endpoint The IdP will send the logout response to this view, which will process it with pysaml2 help and log the user out. Note that the IdP can request a logout even when we didn't initiate the process as a single logout request started by another SP. """ logger.debug('Logout service started') conf = get_config(config_loader_path, request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session), logger=logger) if 'SAMLResponse' in request.GET: # we started the logout logger.debug('Receiving a logout response from the IdP') response = client.logout_response(request.GET['SAMLResponse'], binding=BINDING_HTTP_REDIRECT) state.sync() if response and response[1] == '200 Ok': return django_logout(request, next_page=next_page) else: logger.error('Unknown error during the logout') return HttpResponse('Error during logout') elif 'SAMLRequest' in request.GET: # logout started by the IdP logger.debug('Receiving a logout request from the IdP') subject_id = _get_subject_id(request.session) response, success = client.logout_request(request.GET, subject_id) state.sync() if success: auth.logout(request) assert response[0][0] == 'Location' url = response[0][1] return HttpResponseRedirect(url) elif response is not None: assert response[0][0] == 'Location' url = response[0][1] return HttpResponseRedirect(url) else: logger.error('Unknown error during the logout') return HttpResponse('Error during logout') else: logger.error('No SAMLResponse or SAMLRequest parameter found') raise Http404('No SAMLResponse or SAMLRequest parameter found')
def logout(): """ SAML Logout Request initiator. This view initiates the SAML2 Logout request using the pysaml2 library to create the LogoutRequest. """ # check csrf csrf = request.form['csrf'] if csrf != session.get('_csrft_', None): abort(400) eppn = session.get('user_eppn') user = current_app.central_userdb.get_user_by_eppn(eppn) logger.debug('Logout process started for user {!r}'.format(user)) state = StateCache(session) identity = IdentityCache(session) client = Saml2Client(current_app.saml2_config, state_cache=state, identity_cache=identity) subject_id = _get_name_id(session) if subject_id is None: logger.warning( 'The session does not contain ' 'the subject id for user {!r}'.format(user)) location = current_app.config.get('SAML2_LOGOUT_REDIRECT_URL') else: logouts = client.global_logout(subject_id) loresponse = logouts.values()[0] # loresponse is a dict for REDIRECT binding, and LogoutResponse for SOAP binding if isinstance(loresponse, LogoutResponse): if loresponse.status_ok(): logger.debug('Performing local logout for {!r}'.format(user)) session.clear() location = current_app.config.get('SAML2_LOGOUT_REDIRECT_URL') location = request.form.get('RelayState', location) return redirect(location) else: abort(500) headers_tuple = loresponse[1]['headers'] location = headers_tuple[0][1] logger.info('Redirecting to {!r} to continue the logout process ' 'for user {!r}'.format(location, user)) state.sync() return redirect(location)
def saml_client_for(self, idp_name=None): ''' Given the name of an IdP, return a configuation. The configuration is a hash for use by saml2.config.Config ''' if idp_name not in self.metadata_url_for and idp_name not in self.metadata: raise Exception("Settings for IDP '{}' not found".format(idp_name)) acs_url = self.acs_format % idp_name https_acs_url = self.https_acs_format % idp_name if self.metadata_url_for: rv = requests.get(self.metadata_url_for[idp_name]) metadata = rv.text else: metadata = self.metadata[idp_name] settings = { 'entityid': 'iris', 'metadata': { 'inline': [metadata], }, 'service': { 'sp': { 'endpoints': { 'assertion_consumer_service': [ (acs_url, BINDING_HTTP_REDIRECT), (acs_url, BINDING_HTTP_POST), (https_acs_url, BINDING_HTTP_REDIRECT), (https_acs_url, BINDING_HTTP_POST) ], }, # Don't verify that the incoming requests originate from us via # the built-in cache for authn request ids in pysaml2 'allow_unsolicited': True, # Don't sign authn requests, since signed requests only make # sense in a situation where you control both the SP and IdP 'authn_requests_signed': False, 'logout_requests_signed': True, 'want_assertions_signed': True, 'want_response_signed': True, }, }, } spConfig = Saml2Config() spConfig.load(settings) spConfig.allow_unknown_attributes = True saml_client = Saml2Client(config=spConfig) return saml_client
def idp_initiated(idp_name): logging.warning("idp_name") logging.warning(idp_name) settings = idp_settings[idp_name] settings['service'] = { 'sp': { 'endpoints': { 'assertion_consumer_service': [(request.url, BINDING_HTTP_REDIRECT), (request.url, BINDING_HTTP_POST)], }, # Don't verify that the incoming requests originate from us via # the built-in cache for authn request ids in pysaml2 'allow_unsolicited': True, 'authn_requests_signed': False, 'logout_requests_signed': True, 'want_assertions_signed': True, 'want_response_signed': False, }, } logging.warning(settings) spConfig = Saml2Config() spConfig.load(settings) spConfig.allow_unknown_attributes = True cli = Saml2Client(config=spConfig) try: authn_response = cli.parse_authn_request_response( request.form['SAMLResponse'], entity.BINDING_HTTP_POST) authn_response.get_identity() user_info = authn_response.get_subject() username = user_info.text valid = True except Exception as e: logging.error(e) valid = False return str(e), 401 # "JIT provisioning" if username not in user_store: user_store[username] = { 'first_name': authn_response.ava['FirstName'][0], 'last_name': authn_response.ava['LastName'][0], } user = User(username) login_user(user) # TODO: If it exists, redirect to request.form['RelayState'] return redirect(url_for('user'))
def do_logout_service(request, data, binding, config_loader_path=None, next_page=None, logout_error_template='djangosaml2/logout_error.html'): """SAML Logout Response endpoint The IdP will send the logout response to this view, which will process it with pysaml2 help and log the user out. Note that the IdP can request a logout even when we didn't initiate the process as a single logout request started by another SP. """ logger.debug('Logout service started') conf = get_config(config_loader_path, request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) if 'SAMLResponse' in data: # we started the logout logger.debug('Receiving a logout response from the IdP') response = client.parse_logout_request_response( data['SAMLResponse'], binding) state.sync() return finish_logout(request, response, next_page=next_page) elif 'SAMLRequest' in data: # logout started by the IdP logger.debug('Receiving a logout request from the IdP') subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contain the subject id for user %s. Performing local logout' % request.user) auth.logout(request) return render_to_response(logout_error_template, {}, context_instance=RequestContext(request)) else: http_info = client.handle_logout_request(data['SAMLRequest'], subject_id, binding) state.sync() auth.logout(request) return HttpResponseRedirect(get_location(http_info)) else: logger.error('No SAMLResponse or SAMLRequest parameter found') raise Http404('No SAMLResponse or SAMLRequest parameter found')
def saml_client_for(idp_name=None): ''' Given the name of an IdP, return a configuation. The configuration is a hash for use by saml2.config.Config ''' if idp_name not in metadata_url_for: raise Exception("Settings for IDP '{}' not found".format(idp_name)) acs_url = url_for("idp_initiated", idp_name=idp_name, _external=True) https_acs_url = url_for("idp_initiated", idp_name=idp_name, _external=True, _scheme='https') # SAML metadata changes very rarely. On a production system, # this data should be cached as approprate for your production system. rv = requests.get(metadata_url_for[idp_name]) settings = { 'metadata': { 'inline': [rv.text], }, 'service': { 'sp': { 'endpoints': { 'assertion_consumer_service': [(acs_url, BINDING_HTTP_REDIRECT), (acs_url, BINDING_HTTP_POST), (https_acs_url, BINDING_HTTP_REDIRECT), (https_acs_url, BINDING_HTTP_POST)], }, # Don't verify that the incoming requests originate from us via # the built-in cache for authn request ids in pysaml2 'allow_unsolicited': True, # Don't sign authn requests, since signed requests only make # sense in a situation where you control both the SP and IdP 'authn_requests_signed': False, 'logout_requests_signed': True, 'want_assertions_signed': True, 'want_response_signed': False, }, }, } spConfig = Saml2Config() spConfig.load(settings) spConfig.allow_unknown_attributes = True saml_client = Saml2Client(config=spConfig) return saml_client
def logout(request, config_loader_path=None): """SAML Logout Request initiator This view initiates the SAML2 Logout request using the pysaml2 library to create the LogoutRequest. """ logger.debug('Logout process started') state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contains the subject id for user %s', request.user) result = client.global_logout(subject_id) state.sync() if not result: logger.error("Looks like the user %s is not logged in any IdP/AA", subject_id) return HttpResponseBadRequest("You are not logged in any IdP/AA") if len(result) > 1: logger.error('Sorry, I do not know how to logout from several sources. I will logout just from the first one') for entityid, logout_info in result.items(): if isinstance(logout_info, tuple): binding, http_info = logout_info if binding == BINDING_HTTP_POST: logger.debug('Returning form to the IdP to continue the logout process') body = ''.join(http_info['data']) return HttpResponse(body) elif binding == BINDING_HTTP_REDIRECT: logger.debug('Redirecting to the IdP to continue the logout process') return HttpResponseRedirect(get_location(http_info)) else: logger.error('Unknown binding: %s', binding) return HttpResponseServerError('Failed to log out') else: # We must have had a soap logout return finish_logout(request, logout_info) logger.error('Could not logout because there only the HTTP_REDIRECT is supported') return HttpResponseServerError('Logout Binding not supported')
def __init__( self, srv, lookup, userdb, spconf, url, return_to, cache=None, bindings=None, userinfo=None, samlcache=None, ): """ Construct the class. :param srv: Usually none, but otherwise the oic server. :param return_to: The URL to return to after a successful authentication. """ self.userdb = userdb self.userinfo = userinfo if cache is None: self.cache_outstanding_queries = {} # type: Mapping[str, str] else: self.cache_outstanding_queries = cache UserAuthnMethod.__init__(self, srv) self.return_to = return_to self.idp_query_param = "IdpQuery" if bindings: self.bindings = bindings else: self.bindings = [ BINDING_HTTP_REDIRECT, BINDING_HTTP_POST, BINDING_HTTP_ARTIFACT, ] # TODO Why does this exist? self.verification_endpoint = "" # Configurations for the SP handler. self.sp_conf = importlib.import_module(spconf) config = SPConfig().load(self.sp_conf.CONFIG) self.sp = Saml2Client(config=config) mte = lookup.get_template("unauthorized.mako") argv = {"message": "You are not authorized!"} self.not_authorized = mte.render(**argv) self.samlcache = self.sp_conf.SAML_CACHE
def get_saml_login_request(binding=BINDING_HTTP_REDIRECT): conf = SPConfig() conf.load(copy.deepcopy(sp_conf_dict)) client = Saml2Client(conf) if binding == BINDING_HTTP_REDIRECT: session_id, result = client.prepare_for_authenticate( entityid="test_generic_idp", relay_state="", binding=binding, ) return parse.parse_qs(parse.urlparse( result['headers'][0][1]).query)['SAMLRequest'][0] elif binding == BINDING_HTTP_POST: session_id, request_xml = client.create_authn_request( "http://localhost:9000/idp/sso/post", binding=binding) return base64.b64encode(bytes(request_xml, 'UTF-8'))
def instantiate(p): """Module Instantiation. 0 for success, -1 for failure. """ global CLIENT global ECP # Use IdP info retrieved from the SP when metadata is missing try: CLIENT = Saml2Client(config_file=config.CONFIG) except Exception, err: # Report the error and return -1 for failure. # xxx A more advanced module would retry the database. exception_trace("instantiate", err, LOG()) log(radiusd.L_ERR, str(err)) return -1
def test_basic(): sp = Saml2Client(config_file="servera_conf") with closing(Server(config_file="idp_all_conf")) as idp: srvs = sp.metadata.authn_query_service(idp.config.entityid) destination = srvs[0]["location"] authn_context = requested_authn_context(INTERNETPROTOCOLPASSWORD) subject = Subject(text="abc", name_id=NameID(format=NAMEID_FORMAT_TRANSIENT)) _id, aq = sp.create_authn_query(subject, destination, authn_context) print(aq) assert isinstance(aq, AuthnQuery)
def test_construct_deconstruct_response(): sp = Saml2Client(config_file=dotname("servera_conf")) url = sp.create_discovery_service_request( "http://example.com/saml/disco", "https://example.com/saml/sp.xml", is_passive=True, returnIDParam="foo", return_url="https://example.com/saml/sp/disc") ds = DiscoveryServer(config_file=dotname("disco_conf")) dsr = ds.parse_discovery_service_request(url) args = dict([(key, dsr[key]) for key in ["returnIDParam", "return"]]) url = ds.create_discovery_service_response( entity_id="https://example.com/saml/idp.xml", **args) idp_id = sp.parse_discovery_service_response(url, returnIDParam="foo") assert idp_id == "https://example.com/saml/idp.xml"
def test_construct_deconstruct_request(): sp = Saml2Client(config_file=dotname("servera_conf")) url = sp.create_discovery_service_request( "http://example.com/saml/disco", "https://example.com/saml/sp.xml", is_passive=True, returnIDParam="foo", return_url="https://example.com/saml/sp/disc") print(url) ds = DiscoveryServer(config_file=dotname("disco_conf")) dsr = ds.parse_discovery_service_request(url) # policy is added by the parsing and verifying method assert _eq(list(dsr.keys()), ["return", "entityID", "returnIDParam", "isPassive", "policy"])
def _get_saml_client(domain): acs_url = domain + get_reverse([acs, 'acs', 'django_saml2_auth:acs']) metadata = _get_metadata() authn_requests_signed = settings.SAML2_AUTH.get('AUTHN_REQUESTS_SIGNED', False) saml_settings = { 'metadata': metadata, 'service': { 'sp': { 'endpoints': { 'assertion_consumer_service': [(acs_url, BINDING_HTTP_REDIRECT), (acs_url, BINDING_HTTP_POST)], }, 'allow_unsolicited': True, 'authn_requests_signed': authn_requests_signed, 'logout_requests_signed': True, 'want_assertions_signed': True, 'want_response_signed': False, }, }, } if 'ENTITY_ID' in settings.SAML2_AUTH: saml_settings['entityid'] = settings.SAML2_AUTH['ENTITY_ID'] if 'NAME_ID_FORMAT' in settings.SAML2_AUTH: saml_settings['service']['sp']['name_id_format'] = settings.SAML2_AUTH[ 'NAME_ID_FORMAT'] if 'ACCEPTED_TIME_DIFF' in settings.SAML2_AUTH: saml_settings['accepted_time_diff'] = settings.SAML2_AUTH[ 'ACCEPTED_TIME_DIFF'] if settings.SAML2_AUTH.get('CERT_FILE'): saml_settings['cert_file'] = settings.SAML2_AUTH['CERT_FILE'] if settings.SAML2_AUTH.get('KEY_FILE'): saml_settings['key_file'] = settings.SAML2_AUTH['KEY_FILE'] spConfig = Saml2Config() spConfig.load(saml_settings) spConfig.allow_unknown_attributes = True saml_client = Saml2Client(config=spConfig) return saml_client
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 s_client(self): """ Setup and return the SAML client with specified config """ acs_url = url_for('sso', _scheme='https', _external=True) logger.debug('SSO ACS URL: {}'.format(acs_url)) logout_url = url_for('logout', _scheme='https', _external=True) try: with open(self.meta_file, 'r') as meta_fh: if len(meta_fh.read()) > 0: pass except FileError as err: res = requests.get(self.meta_url) with open(self.meta_file, 'w') as meta_fh: meta_fh.write(res.text) ###***review all of these settings settings = { 'metadata': { "local": [self.meta_file] }, 'service': { 'sp': { 'name_id_format': 'None', 'endpoints': { 'assertion_consumer_service': [(acs_url, BINDING_HTTP_REDIRECT), (acs_url, BINDING_HTTP_POST)], 'single_logout_service': [(logout_url, BINDING_HTTP_REDIRECT)] }, ###***update some of these if possible 'allow_unsolicited': True, #'allow_unknown_attributes': True, 'authn_requests_signed': False, 'logout_requests_signed': True, 'want_assertions_signed': True, 'want_response_signed': False, 'attribute_map_dir': './attributemaps', }, }, } sp_config = Saml2Config() sp_config.load(settings) sp_config.entityid = self.entity_id sp_config.allow_unknown_attributes = True client = Saml2Client(config=sp_config) return client
def saml2_client_for(idp_name=None): ''' Given the name of an Identity Provider look up the Saml2Configuration and build a SAML Client. Return these. ''' # SAML metadata changes very rarely, check for cached version first config = Saml2Configuration.objects.get(slug=idp_name) metadata = None if config: metadata = config.cached_metadata if not metadata: r = requests.get(config.metadata_conf_url) metadata = r.text origin = getattr(settings, 'HTTP_ORIGIN').split('://')[1] https_acs_url = 'https://' + origin + reverse('assertion_consumer_service', args=[idp_name]) setting = { 'metadata': { 'inline': [metadata], }, 'entityid': "badgrserver", 'service': { 'sp': { 'endpoints': { 'assertion_consumer_service': [ (https_acs_url, BINDING_HTTP_POST) ], }, # Don't verify that the incoming requests originate from us via # the built-in cache for authn request ids in pysaml2 'allow_unsolicited': True, # Don't sign authn requests, since signed requests only make # sense in a situation where you control both the SP and IdP 'authn_requests_signed': False, 'logout_requests_signed': True, 'want_assertions_signed': True, 'want_response_signed': False, }, }, } spConfig = Saml2Config() spConfig.load(setting) spConfig.allow_unknown_attributes = True saml_client = Saml2Client(config=spConfig) return saml_client, config
def get_saml_client(org): """ Return SAML configuration. The configuration is a hash for use by saml2.config.Config """ metadata_url = org.get_setting("auth_saml_metadata_url") entity_id = org.get_setting("auth_saml_entity_id") acs_url = url_for("saml_auth.idp_initiated", org_slug=org.slug, _external=True) saml_settings = { 'metadata': { "remote": [{ "url": metadata_url }] }, 'service': { 'sp': { 'endpoints': { 'assertion_consumer_service': [(acs_url, BINDING_HTTP_REDIRECT), (acs_url, BINDING_HTTP_POST)], }, # Don't verify that the incoming requests originate from us via # the built-in cache for authn request ids in pysaml2 'allow_unsolicited': True, # Don't sign authn requests, since signed requests only make # sense in a situation where you control both the SP and IdP 'authn_requests_signed': False, 'logout_requests_signed': True, 'want_assertions_signed': True, 'want_response_signed': False, }, }, } if entity_id is not None and entity_id != "": saml_settings['entityid'] = entity_id sp_config = Saml2Config() sp_config.load(saml_settings) sp_config.allow_unknown_attributes = True saml_client = Saml2Client(config=sp_config) return saml_client
def get_saml_client(self, handler): acs_url = self.get_current_domain(handler) + handler.reverse_url( 'saml2_acs_handler') if self.saml2_metadata_filename: metadata = {'local': [self.saml2_metadata_filename]} elif self.saml2_metadata_url: metadata = { 'remote': [ { "url": self.saml2_metadata_url, }, ] } else: raise web.HTTPError(500, 'No metadata provided') saml_settings = { 'metadata': metadata, 'service': { 'sp': { 'endpoints': { 'assertion_consumer_service': [(acs_url, BINDING_HTTP_REDIRECT), (acs_url, BINDING_HTTP_POST)], }, 'allow_unsolicited': True, 'authn_requests_signed': False, 'logout_requests_signed': True, 'want_assertions_signed': True, 'want_response_signed': False, }, }, } if self.saml2_entity_id: saml_settings['entityid'] = self.saml2_entity_id if self.saml2_name_id_format: saml_settings['service']['sp'][ 'name_id_format'] = self.saml2_name_id_format spConfig = Saml2Config() spConfig.load(saml_settings) spConfig.allow_unknown_attributes = True saml_client = Saml2Client(config=spConfig) return saml_client
def echo_attributes(request, config_loader_path=None, template='djangosaml2/echo_attributes.html'): """Example view that echo the SAML attributes of an user""" state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) subject_id = _get_subject_id(request.session) try: identity = client.users.get_identity(subject_id, check_not_on_or_after=False) except AttributeError: return HttpResponse("No active SAML identity found. Are you sure you have logged in via SAML?") return render(request, template, {'attributes': identity[0]})
def create_logout_request(subject_id, destination, issuer_entity_id, req_entity_id, sign=True): config = SPConfig() config.load(sp_config) sp_client = Saml2Client(config=config) # construct a request logout_request = samlp.LogoutRequest(id='a123456', version=VERSION, destination=destination, issuer=saml.Issuer( text=req_entity_id, format=saml.NAMEID_FORMAT_ENTITY), name_id=saml.NameID(text=subject_id)) return logout_request
def test_request_response(): sp = Saml2Client(config_file="servera_conf") with closing(Server(config_file="idp_all_conf")) as idp: binding, destination = sp.pick_binding("name_id_mapping_service", entity_id=idp.config.entityid) policy = NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT, sp_name_qualifier="urn:mace:swamid:junk", allow_create="true") nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar") mid, nmr = sp.create_name_id_mapping_request(policy, nameid, destination) print nmr args = sp.use_soap(nmr, destination) # ------- IDP ------------ req = idp.parse_name_id_mapping_request(args["data"], binding) in_response_to = req.message.id name_id = NameID(format=NAMEID_FORMAT_PERSISTENT, text="foobar") idp_response = idp.create_name_id_mapping_response( name_id, in_response_to=in_response_to) print idp_response ht_args = sp.use_soap(idp_response) # ------- SP ------------ _resp = sp.parse_name_id_mapping_request_response( ht_args["data"], binding) print _resp.response r_name_id = _resp.response.name_id assert r_name_id.format == NAMEID_FORMAT_PERSISTENT assert r_name_id.text == "foobar"
def _get_saml_client(domain): acs_url = domain + get_reverse([acs, 'acs', 'django_saml2_auth:acs']) saml_settings = { 'metadata': {}, 'service': { 'sp': { 'endpoints': { 'assertion_consumer_service': [(acs_url, BINDING_HTTP_REDIRECT), (acs_url, BINDING_HTTP_POST)], }, 'allow_unsolicited': True, 'authn_requests_signed': False, 'logout_requests_signed': True, 'want_assertions_signed': True, 'want_response_signed': False, }, }, } if 'METADATA_AUTO_CONF_URL' in settings.SAML2_AUTH: saml_settings['metadata']['remote'] = settings.SAML2_AUTH[ 'METADATA_AUTO_CONF_URL'] if 'METADATA_AUTO_CONF_INLINE' in settings.SAML2_AUTH: saml_settings['metadata']['inline'] = settings.SAML2_AUTH[ 'METADATA_AUTO_CONF_INLINE'] if 'METADATA_AUTO_CONF_LOCAL' in settings.SAML2_AUTH: saml_settings['metadata']['local'] = settings.SAML2_AUTH[ 'METADATA_AUTO_CONF_LOCAL'] if 'ENTITY_ID' in settings.SAML2_AUTH: saml_settings['entityid'] = settings.SAML2_AUTH['ENTITY_ID'] if 'NAME_ID_FORMAT' in settings.SAML2_AUTH: saml_settings['service']['sp']['name_id_format'] = settings.SAML2_AUTH[ 'NAME_ID_FORMAT'] spConfig = Saml2Config() spConfig.load(saml_settings) spConfig.allow_unknown_attributes = True saml_client = Saml2Client(config=spConfig) return saml_client
def test_base_request(): sp = Saml2Client(config_file="servera_conf") idp = Server(config_file="idp_all_conf") binding, destination = sp.pick_binding("name_id_mapping_service", entity_id=idp.config.entityid) policy = NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT, sp_name_qualifier="urn:mace:swamid:junk", allow_create="true") nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar") nmr = sp.create_name_id_mapping_request(policy, nameid, destination) print nmr assert isinstance(nmr, NameIDMappingRequest)
async def saml_client(kind='admin'): if kind == 'admin': url = SAML_METADATA_URL_ADMIN acs_url = SAML_ACS_URL_ADMIN entity_id = SAML_ENTITY_ID_ADMIN else: url = SAML_METADATA_URL_FILER acs_url = SAML_ACS_URL_ADMIN entity_id = SAML_ENTITY_ID_FILER meta = await get_metadata(url) settings = { 'entityid': entity_id, 'metadata': { 'inline': [meta], }, 'service': { 'sp': { 'endpoints': { 'assertion_consumer_service': [ (acs_url, BINDING_HTTP_REDIRECT), (acs_url, BINDING_HTTP_POST), ], }, # Don't verify that the incoming requests originate from us via # the built-in cache for authn request ids in pysaml2 'allow_unsolicited': True, # Don't sign authn requests, since signed requests only make # sense in a situation where you control both the SP and IdP 'authn_requests_signed': False, 'logout_requests_signed': True, 'want_assertions_signed': True, 'want_response_signed': False, }, }, } spConfig = Saml2Config() spConfig.load(settings) spConfig.allow_unknown_attributes = True saml_client = Saml2Client(config=spConfig) return saml_client
def test_flow(): sp = Saml2Client(config_file="servera_conf") try: with closing(Server(config_file="idp_conf_mdb")) as idp1: with closing(Server(config_file="idp_conf_mdb")) as idp2: # clean out database idp1.ident.mdb.db.drop() # -- dummy request --- req_id, orig_req = sp.create_authn_request( idp1.config.entityid) # == Create an AuthnRequest response rinfo = idp1.response_args(orig_req, [BINDING_HTTP_POST]) #name_id = idp1.ident.transient_nameid("id12", rinfo["sp_entity_id"]) resp = idp1.create_authn_response( { "eduPersonEntitlement": "Short stop", "surName": "Jeter", "givenName": "Derek", "mail": "*****@*****.**", "title": "The man" }, userid="jeter", authn=AUTHN, **rinfo) # What's stored away is the assertion a_info = idp2.session_db.get_assertion(resp.assertion.id) # Make sure what I got back from MongoDB is the same as I put in assert a_info["assertion"] == resp.assertion # By subject nid = resp.assertion.subject.name_id _assertion = idp2.session_db.get_assertions_by_subject(nid) assert len(_assertion) == 1 assert _assertion[0] == resp.assertion nids = idp2.ident.find_nameid("jeter") assert len(nids) == 1 except ConnectionFailure: pass
def login(request, config_loader_path=None, wayf_template='djangosaml2/wayf.html', authorization_error_template='djangosaml2/auth_error.html'): """SAML Authorization Request initiator This view initiates the SAML2 Authorization handshake using the pysaml2 library to create the AuthnRequest. It uses the SAML 2.0 Http Redirect protocol binding. """ logger.debug('Login process started') came_from = request.GET.get('next', settings.LOGIN_REDIRECT_URL) if not request.user.is_anonymous(): logger.debug('User is already logged in') return render_to_response(authorization_error_template, { 'came_from': came_from, }, context_instance=RequestContext(request)) selected_idp = request.GET.get('idp', None) conf = get_config(config_loader_path, request) # is a embedded wayf needed? idps = conf.idps() if selected_idp is None and len(idps) > 1: logger.debug('A discovery process is needed') return render_to_response(wayf_template, { 'available_idps': idps.items(), 'came_from': came_from, }, context_instance=RequestContext(request)) client = Saml2Client(conf, logger=logger) try: (session_id, result) = client.authenticate( entityid=selected_idp, relay_state=came_from, binding=BINDING_HTTP_REDIRECT, ) except TypeError, e: logger.error('Unable to know which IdP to use') return HttpResponse(unicode(e))
def _get_saml_client(domain): acs_url = domain + get_reverse([acs, 'acs', 'django_saml2_auth:acs']) metadata = _get_metadata() saml_settings = { 'metadata': metadata, 'service': { 'sp': { 'endpoints': { 'assertion_consumer_service': [(acs_url, BINDING_HTTP_REDIRECT), (acs_url, BINDING_HTTP_POST)], }, 'allow_unsolicited': True, 'authn_requests_signed': False, 'logout_requests_signed': True, 'want_assertions_signed': True, 'want_response_signed': False, }, }, } if 'ENTITY_ID' in settings.SAML2_AUTH: saml_settings['entityid'] = settings.SAML2_AUTH['ENTITY_ID'] if 'NAME_ID_FORMAT' in settings.SAML2_AUTH: saml_settings['service']['sp']['name_id_format'] = settings.SAML2_AUTH[ 'NAME_ID_FORMAT'] if 'WANT_ASSERTIONS_SIGNED' in settings.SAML2_AUTH: saml_settings['service']['sp'][ 'want_assertions_signed'] = settings.SAML2_AUTH[ 'WANT_ASSERTIONS_SIGNED'] if 'WANT_RESPONSE_SIGNED' in settings.SAML2_AUTH: saml_settings['service']['sp'][ 'want_response_signed'] = settings.SAML2_AUTH[ 'WANT_RESPONSE_SIGNED'] spConfig = Saml2Config() spConfig.load(saml_settings) spConfig.allow_unknown_attributes = True saml_client = Saml2Client(config=spConfig) return saml_client
def get_login_url(self): """returns the url for login""" client = Saml2Client(config_file=config_file) if not self.entityid: idps = client.metadata.with_descriptor("idpsso") self.entityid = idps.keys()[0] bindings = [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST] binding, destination = client.pick_binding("single_sign_on_service", bindings, "idpsso", entity_id=entityid) req_id, req = client.create_authn_request(destination, binding=BINDING_HTTP_POST) relay_state = web2py_uuid().replace("-", "") http_args = client.apply_binding(binding, str(req), destination, relay_state=relay_state) return http_args["headers"]["Location"]