def outgoing(self, response, org_response, instance): """ An authentication response has been received and now an authentication response from this server should be constructed. :param response: The Authentication response :param instance: SP instance that received the authentication response :return: response """ _idp = self.create_SamlIDP(instance.environ, instance.start_response, self.outgoing) _state = instance.sp.state[response.in_response_to] orig_authn_req, relay_state, req_args = instance.sp.state[_state] # The Subject NameID try: subject = response.get_subject() except: pass resp_args = _idp.idp.response_args(orig_authn_req) try: _authn_info = response.authn_info()[0] AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(_authn_info[0]), username_password_authn_dummy, 0, self.issuer) _authn = AUTHN_BROKER.get_authn_by_accr(_authn_info[0]) #_authn = {"class_ref": _authn_info[0], "authn_auth": self.issuer} except: AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(UNSPECIFIED), username_password_authn_dummy, 0, self.issuer) _authn = AUTHN_BROKER.get_authn_by_accr(UNSPECIFIED) identity = response.ava if identity is None and response.response.encrypted_assertion is not None: #Add dummy value identity = {"uid": "dummyuser"} # Will signed the response by default resp = _idp.construct_authn_response(identity, userid="dummyuser", authn=_authn, name_id=None, resp_args=resp_args, relay_state=relay_state, sign_response=True, org_resp=response, org_xml_response=org_response) return resp
def _prepare_server(self): """ Setup server """ self.idp_config = Saml2Config() self.BASE = '{}://{}:{}'.format(self._mode, self._config.get('host'), self._config.get('port')) if 'entityid' not in self._config: # as fallback for entityid use host:port string self._config['entityid'] = self.BASE self.idp_config.load(cnf=self._idp_config()) setattr( self.idp_config, 'attribute_converters', ac_factory('testenv/attributemaps', **{'override_types': self._all_attributes})) self.server = SpidServer(config=self.idp_config) self._setup_app_routes() # setup custom methods in order to # prepare the login form and verify the challenge (optional) # for every spid level (1-2-3) self.authn_broker = AuthnBroker() for index, _level in enumerate(self._spid_levels): self.authn_broker.add( authn_context_class_ref(_level), getattr(self, '_verify_spid_{}'.format(index + 1)))
def handle_authn_request(self, saml_request, relay_state, binding, userid): self.authn_req = self.idp.parse_authn_request(saml_request, binding) _encrypt_cert = encrypt_cert_from_item(self.authn_req.message) self.binding_out, self.destination = self.idp.pick_binding( "assertion_consumer_service", bindings=None, entity_id=self.authn_req.message.issuer.text, request=self.authn_req.message) resp_args = self.idp.response_args(self.authn_req.message) AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(PASSWORD), username_password_authn_dummy, 10, "http://test.idp.se") AUTHN_BROKER.get_authn_by_accr(PASSWORD) resp_args["authn"] = AUTHN_BROKER.get_authn_by_accr(PASSWORD) _resp = self.idp.create_authn_response(TestIdP.USERS[userid], userid=userid, encrypt_cert=_encrypt_cert, encrypt_assertion_self_contained=True, encrypted_advice_attributes=True, **resp_args) kwargs = {} http_args = self.idp.apply_binding(BINDING_HTTP_POST, "%s" % _resp, self.destination, relay_state, response=True, **kwargs) action, body = get_post_action_body(http_args["data"][3]) return action, urllib.urlencode(body)
def handle_auth_req(self, saml_request, relay_state, binding, userid): auth_req = self.parse_authn_request(saml_request, binding) binding_out, destination = self.pick_binding( 'assertion_consumer_service', entity_id=auth_req.message.issuer.text, request=auth_req.message) resp_args = self.response_args(auth_req.message) authn_broker = AuthnBroker() authn_broker.add(authn_context_class_ref(PASSWORD), lambda: None, 10, 'unittest_idp.xml') authn_broker.get_authn_by_accr(PASSWORD) resp_args['authn'] = authn_broker.get_authn_by_accr(PASSWORD) _resp = self.create_authn_response(self.user_db[userid], userid=userid, **resp_args) http_args = self.apply_binding(BINDING_HTTP_POST, '%s' % _resp, destination, relay_state, response=True) url = http_args['url'] saml_response = base64.b64encode(str(_resp).encode("utf-8")) resp = {'SAMLResponse': saml_response, 'RelayState': relay_state} return url, resp
def get_authn_response(self, idp_config, identity): with closing(SamlServer(idp_config)) as server: name_id = server.ident.transient_nameid( "urn:mace:example.com:saml:roland:idp", "id12") authn_context_ref = authn_context_class_ref( AUTHN_PASSWORD_PROTECTED) authn_context = AuthnContext( authn_context_class_ref=authn_context_ref) locality = saml.SubjectLocality() locality.address = "172.31.25.30" authn_statement = AuthnStatement( subject_locality=locality, authn_instant=datetime.now().isoformat(), authn_context=authn_context, session_index="id12") return server.create_authn_response( identity, "id12", # in_response_to self. sp_acs_location, # consumer_url. config.sp.endpoints.assertion_consumer_service:["acs_endpoint"] self.sp_acs_location, # sp_entity_id name_id=name_id, sign_assertion=True, sign_response=True, authn_statement=authn_statement)
def get(self, request, *args, **kwargs): passed_data = request.POST if request.method == 'POST' else request.GET # get sp information from the parameters try: sp_entity_id = passed_data['sp'] except KeyError as e: return HttpResponseBadRequest(e) try: sp_config = settings.SAML_IDP_SPCONFIG[sp_entity_id] except Exception: raise ImproperlyConfigured("No config for SP %s defined in SAML_IDP_SPCONFIG" % sp_entity_id) binding_out, destination = self.IDP.pick_binding( service="assertion_consumer_service", entity_id=sp_entity_id) processor = self.get_processor(sp_config) # Check if user has access to the service of this SP if not processor.has_access(request.user): raise PermissionDenied("You do not have access to this resource") identity = self.get_identity(processor, request.user, sp_config) req_authn_context = PASSWORD AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(req_authn_context), "") # Construct SamlResponse messages try: name_id_formats = self.IDP.config.getattr("name_id_format", "idp") or [NAMEID_FORMAT_UNSPECIFIED] name_id = NameID(format=name_id_formats[0], text=request.user.username) authn = AUTHN_BROKER.get_authn_by_accr(req_authn_context) sign_response = self.IDP.config.getattr("sign_response", "idp") or False sign_assertion = self.IDP.config.getattr("sign_assertion", "idp") or False authn_resp = self.IDP.create_authn_response( identity=identity, in_response_to="IdP_Initiated_Login", destination=destination, sp_entity_id=sp_entity_id, userid=request.user.username, name_id=name_id, authn=authn, sign_response=sign_response, sign_assertion=sign_assertion, **passed_data) except Exception as excp: return HttpResponseServerError(excp) # Return as html with self-submitting form. http_args = self.IDP.apply_binding( binding=binding_out, msg_str="%s" % authn_resp, destination=destination, relay_state=passed_data['RelayState'], response=True) return HttpResponse(http_args['data'])
def get_authn(self, req_info=None): if req_info: req_authn_context = req_info.message.requested_authn_context else: req_authn_context = PASSWORD broker = AuthnBroker() broker.add(authn_context_class_ref(req_authn_context), "") return broker.get_authn_by_accr(req_authn_context)
def main(): global IDP global AUTHN_BROKER global LOOKUP global args sys.path.insert(0, os.getcwd()) from wsgiref.simple_server import make_server parser = argparse.ArgumentParser() parser.add_argument('-p', dest='path', help='Path to configuration file.') parser.add_argument('-v', dest='valid', help="How long, in days, the metadata is valid from the time of creation") parser.add_argument('-c', dest='cert', help='certificate') parser.add_argument('-i', dest='id', help="The ID of the entities descriptor") parser.add_argument('-k', dest='keyfile', help="A file with a key to sign the metadata with") parser.add_argument('-n', dest='name') parser.add_argument('-s', dest='sign', action='store_true', help="sign the metadata") parser.add_argument('-m', dest='mako_root', default="./") parser.add_argument(dest="config") args = parser.parse_args() AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(PASSWORD), username_password_authn, 10, "http://%s" % socket.gethostname()) AUTHN_BROKER.add(authn_context_class_ref(UNSPECIFIED), "", 0, "http://%s" % socket.gethostname()) CONFIG = importlib.import_module(args.config) IDP = server.Server(args.config, cache=Cache()) IDP.ticket = {} _rot = args.mako_root LOOKUP = TemplateLookup(directories=[_rot + 'templates', _rot + 'htdocs'], module_directory=_rot + 'modules', input_encoding='utf-8', output_encoding='utf-8') HOST = CONFIG.HOST PORT = CONFIG.PORT SRV = make_server(HOST, PORT, application) print "IdP listening on %s:%s" % (HOST, PORT) SRV.serve_forever()
def setup_authn_broker(self, base_url, sphandler, authorization): ab = AuthnBroker() sphandler.sp_authentication = SpAuthentication(self, sphandler) cas_auth = CasAuth(self, self.cas_server, self.service_url) password_auth = PasswordYubikeyAuth(self, self.passwd, password=True, yubikey=False) yubikey_auth = PasswordYubikeyAuth(self, self.passwd, password=False, yubikey=True) password_yubikey_auth = PasswordYubikeyAuth(self, self.passwd, password=True, yubikey=True) for authkey, value in authorization.items(): level = str(value[IdPHandler.AUTHORIZATION_WEIGHT]) url = value[IdPHandler.AUTHORIZATION_URL] acr = value[IdPHandler.AUTHORIZATION_ACR] user_info = value[IdPHandler.AUTHORIZATION_USER_INFO] if authkey == IdPHandler.AUTHORIZATION_SAML: sphandler.sp_authentication.user_info(user_info) ab.add(acr, sphandler.sp_authentication, level, url) elif authkey == IdPHandler.AUTHORIZATION_CAS: cas_auth.user_info(user_info) ab.add(acr, cas_auth, level, url) elif authkey == IdPHandler.AUTHORIZATION_PASSWORD_YUBIKEY: password_yubikey_auth.user_info(user_info) ab.add(acr, password_yubikey_auth, level, url) elif authkey == IdPHandler.AUTHORIZATION_PASSWORD: password_auth.user_info(user_info) ab.add(acr, password_auth, level, url) elif authkey == IdPHandler.AUTHORIZATION_YUBIKEY: yubikey_auth.user_info(user_info) ab.add(acr, yubikey_auth, level, url) elif authkey == IdPHandler.AUTHORIZATION_MULTIPLEAUTHN: authn_list = [] for m_items in value[IdPHandler.AUTHENTICATION_AUTHNLIST]: m_authkey = m_items[IdPHandler.AUTHORIZATION_ACR] if m_authkey == IdPHandler.AUTHORIZATION_SAML: authn_list.append(sphandler.sp_authentication) elif m_authkey == IdPHandler.AUTHORIZATION_CAS: authn_list.append(cas_auth) elif m_authkey == IdPHandler.AUTHORIZATION_PASSWORD_YUBIKEY: authn_list.append(password_yubikey_auth) elif m_authkey == IdPHandler.AUTHORIZATION_PASSWORD: authn_list.append(password_auth) elif m_authkey == IdPHandler.AUTHORIZATION_YUBIKEY: authn_list.append(yubikey_auth) ab.add(acr, MultipleAuthentication(self, authn_list, user_info), level, url) else: ab.add(authn_context_class_ref(UNSPECIFIED), UnspecifiedAuth(self), level, url) return ab
def test_authn_1(): ac = authn_context_class_ref(PASSWORDPROTECTEDTRANSPORT) rac = requested_authn_context(PASSWORDPROTECTEDTRANSPORT) authn = AuthnBroker() target = "https://example.org/login" authn.add(ac, target, 1, "http://www.example.com") result = authn.pick(rac) assert len(result) == 1 method, reference = result[0] assert target == method
def test_authn_3(): authn = AuthnBroker() level = 0 for ref in [AL1, AL2, AL3, AL4]: level += 4 ac = authn_context_class_ref(ref) authn.add(ac, REF2METHOD[ref], level, "https://www.example.com/%s" % "al%d" % level) rac = requested_authn_context(AL1, "minimum") info = authn.pick(rac) assert len(info) == 4 method, ref = info[0] assert REF2METHOD[AL1] == method rac = requested_authn_context(AL2, "minimum") info = authn.pick(rac) assert len(info) == 3 method, ref = info[0] assert REF2METHOD[AL2] == method rac = requested_authn_context(AL3, "minimum") info = authn.pick(rac) assert len(info) == 2 method, ref = info[0] assert REF2METHOD[AL3] == method rac = requested_authn_context(AL4, "minimum") info = authn.pick(rac) assert len(info) == 1 method, ref = info[0] assert REF2METHOD[AL4] == method rac = requested_authn_context(AL1, "exact") info = authn.pick(rac) assert len(info) == 1 method, ref = info[0] assert REF2METHOD[AL1] == method rac = requested_authn_context(AL1, "better") info = authn.pick(rac) assert len(info) == 3
def handle_auth_req(self, saml_request, relay_state, binding, userid, response_binding=BINDING_HTTP_POST): """ Handles a SAML request, validates and creates a SAML response. :type saml_request: str :type relay_state: str :type binding: str :type userid: str :rtype: :param saml_request: :param relay_state: RelayState is a parameter used by some SAML protocol implementations to identify the specific resource at the resource provider in an IDP initiated single sign on scenario. :param binding: :param userid: The user identification. :return: A tuple with """ auth_req = self.parse_authn_request(saml_request, binding) binding_out, destination = self.pick_binding( 'assertion_consumer_service', bindings=[response_binding], entity_id=auth_req.message.issuer.text, request=auth_req.message) resp_args = self.response_args(auth_req.message) authn_broker = AuthnBroker() authn_broker.add(authn_context_class_ref(PASSWORD), lambda: None, 10, 'unittest_idp.xml') authn_broker.get_authn_by_accr(PASSWORD) resp_args['authn'] = authn_broker.get_authn_by_accr(PASSWORD) _resp = self.create_authn_response(self.user_db[userid], userid=userid, **resp_args) if response_binding == BINDING_HTTP_POST: saml_response = base64.b64encode(str(_resp).encode("utf-8")) resp = {"SAMLResponse": saml_response, "RelayState": relay_state} elif response_binding == BINDING_HTTP_REDIRECT: http_args = self.apply_binding(response_binding, '%s' % _resp, destination, relay_state, response=True) resp = dict(parse_qsl(urlparse(dict(http_args["headers"])["Location"]).query)) return destination, resp
def __create_authn_response(self, saml_request, relay_state, binding, userid, response_binding=BINDING_HTTP_POST): """ Handles a SAML request, validates and creates a SAML response but does not apply the binding to encode it. :type saml_request: str :type relay_state: str :type binding: str :type userid: str :rtype: tuple [string, saml2.samlp.Response] :param saml_request: :param relay_state: RelayState is a parameter used by some SAML protocol implementations to identify the specific resource at the resource provider in an IDP initiated single sign on scenario. :param binding: :param userid: The user identification. :return: A tuple containing the destination and instance of saml2.samlp.Response """ auth_req = self.parse_authn_request(saml_request, binding) binding_out, destination = self.pick_binding( 'assertion_consumer_service', bindings=[response_binding], entity_id=auth_req.message.issuer.text, request=auth_req.message) resp_args = self.response_args(auth_req.message) authn_broker = AuthnBroker() authn_broker.add(authn_context_class_ref(PASSWORD), lambda: None, 10, 'unittest_idp.xml') authn_broker.get_authn_by_accr(PASSWORD) resp_args['authn'] = authn_broker.get_authn_by_accr(PASSWORD) resp = self.create_authn_response(self.user_db[userid], userid=userid, **resp_args) return destination, resp
def __init__(self, config): baseurl = config['protocol'] + "://" + config['url'] log.info("Application baseurl: " + baseurl) # Update URLs with base for pro in SSIXASAMLProvider_SERVICE_EP: for serv in SSIXASAMLProvider_SERVICE_EP[pro]['endpoints']: if 'service' in serv: for ep in SSIXASAMLProvider_SERVICE_EP[pro]['endpoints'][ serv]: SSIXASAMLProvider_SERVICE_EP[pro]['endpoints'][serv][ 0] = baseurl + ep[0] # Update SAML config config['saml_config'].update({'service': SSIXASAMLProvider_SERVICE_EP}) cfg = SAMLIdpConfig(config['saml_config']) # Create SAML IdP server - Init super class after config preparation super(SSIXASAMLProvider, self).__init__(config=cfg, cache=Cache()) # Add authn broker self.authn_broker = AuthnBroker() self.userinfodb = SAMLUserInfoDB() i = BlockchainAuthMethod(baseurl + "/saml", config['proxymode'], config, self.userinfodb, config['trustmodel']) self.authn_broker.add(authn_context_class_ref(UNSPECIFIED), i, 3, {i.acr}) # Add attribute converters self.config.attribute_converters = ac_factory() # Add metadata self.metadata = self.create_metadata(config['saml_config']['metadata']) # Response bindings that are offered self.response_bindings = [BINDING_HTTP_POST, BINDING_HTTP_REDIRECT] # Default claims self.defaul_claims = copy.deepcopy( config['saml_config']['default_claims'])
def test_acs_with_authn_response_includes_subjectLocality(self): self._skip_if_xmlsec_binary_missing() self.config.use_signed_authn_request = True self.config.save() with override_settings(SAML_KEY_FILE=self.ipd_key_path, SAML_CERT_FILE=self.ipd_cert_path): saml2config = self.config sp_config = config.SPConfig() sp_config.load(create_saml_config_for(saml2config)) sp_metadata = create_metadata_string('', config=sp_config, sign=True) idp_config = self.get_idp_config(sp_metadata) identity = { "eduPersonAffiliation": ["staff", "member"], "surName": ["Jeter"], "givenName": ["Derek"], "mail": ["*****@*****.**"], "title": ["shortstop"] } with closing(SamlServer(idp_config)) as server: name_id = server.ident.transient_nameid( "urn:mace:example.com:saml:roland:idp", "id12") authn_context_ref = authn_context_class_ref( AUTHN_PASSWORD_PROTECTED) authn_context = AuthnContext( authn_context_class_ref=authn_context_ref) locality = saml.SubjectLocality() locality.address = "172.31.25.30" authn_statement = AuthnStatement( subject_locality=locality, authn_instant=datetime.now().isoformat(), authn_context=authn_context, session_index="id12") authn_response = server.create_authn_response( identity, "id12", # in_response_to self. sp_acs_location, # consumer_url. config.sp.endpoints.assertion_consumer_service:["acs_endpoint"] self.sp_acs_location, # sp_entity_id name_id=name_id, sign_assertion=True, sign_response=True, authn_statement=authn_statement) base64_encoded_response_metadata = base64.b64encode( authn_response.encode('utf-8')) base_64_utf8_response_metadata = base64_encoded_response_metadata.decode( 'utf-8') request = self.client.post( reverse('assertion_consumer_service', kwargs={'idp_name': self.config.slug}), {'SAMLResponse': base_64_utf8_response_metadata})
def get(self, request, *args, **kwargs): binding = request.session.get('Binding', BINDING_HTTP_POST) # Parse incoming request try: req_info = self.IDP.parse_authn_request( request.session['SAMLRequest'], binding) except Exception as excp: return self.handle_error(request, exception=excp) # Signed request for HTTP-REDIRECT if "SigAlg" in request.session and "Signature" in request.session: _certs = self.IDP.metadata.certs(req_info.message.issuer.text, "any", "signing") verified_ok = False for cert in _certs: # TODO implement # if verify_redirect_signature(_info, self.IDP.sec.sec_backend, cert): # verified_ok = True # break pass if not verified_ok: return self.handle_error( request, extra_message="Message signature verification failure", status=400) # Gather response arguments try: resp_args = self.IDP.response_args(req_info.message) except (UnknownPrincipal, UnsupportedBinding) as excp: return self.handle_error(request, exception=excp, status=400) try: sp_config = settings.SAML_IDP_SPCONFIG[resp_args['sp_entity_id']] except Exception: return self.handle_error( request, exception=ImproperlyConfigured( "No config for SP %s defined in SAML_IDP_SPCONFIG" % resp_args['sp_entity_id']), status=400) processor = self.get_processor(resp_args['sp_entity_id'], sp_config) # Check if user has access to the service of this SP if not processor.has_access(request): return self.handle_error( request, exception=PermissionDenied( "You do not have access to this resource"), status=403) identity = self.get_identity(processor, request.user, sp_config) req_authn_context = req_info.message.requested_authn_context or PASSWORD AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(req_authn_context), "") user_id = processor.get_user_id(request.user) # Construct SamlResponse message try: authn_resp = self.IDP.create_authn_response( identity=identity, userid=user_id, name_id=NameID(format=resp_args['name_id_policy'].format, sp_name_qualifier=resp_args['sp_entity_id'], text=user_id), authn=AUTHN_BROKER.get_authn_by_accr(req_authn_context), sign_response=self.IDP.config.getattr("sign_response", "idp") or False, sign_assertion=self.IDP.config.getattr("sign_assertion", "idp") or False, **resp_args) except Exception as excp: return self.handle_error(request, exception=excp, status=500) http_args = self.IDP.apply_binding( binding=resp_args['binding'], msg_str="%s" % authn_resp, destination=resp_args['destination'], relay_state=request.session['RelayState'], response=True) logger.debug('http args are: %s' % http_args) return self.render_response(request, processor, http_args)
def login_process(request): """ View which processes the actual SAML request and returns a self-submitting form with the SAML response. The login_required decorator ensures the user authenticates first on the IdP using 'normal' ways. """ # Construct server with config from settings dict conf = IdPConfig() conf.load(copy.deepcopy(settings.SAML_IDP_CONFIG)) IDP = Server(config=conf) # Parse incoming request try: req_info = IDP.parse_authn_request(request.session['SAMLRequest'], BINDING_HTTP_POST) except Exception as excp: return HttpResponseBadRequest(excp) # TODO this is taken from example, but no idea how this works or whats it does. Check SAML2 specification? # Signed request for HTTP-REDIRECT if "SigAlg" in request.session and "Signature" in request.session: _certs = IDP.metadata.certs(req_info.message.issuer.text, "any", "signing") verified_ok = False for cert in _certs: # TODO implement #if verify_redirect_signature(_info, IDP.sec.sec_backend, cert): # verified_ok = True # break pass if not verified_ok: return HttpResponseBadRequest( "Message signature verification failure") binding_out, destination = IDP.pick_binding( service="assertion_consumer_service", entity_id=req_info.message.issuer.text) # Gather response arguments try: resp_args = IDP.response_args(req_info.message) except (UnknownPrincipal, UnsupportedBinding) as excp: return HttpResponseServerError(excp) try: sp_config = settings.SAML_IDP_SPCONFIG[resp_args['sp_entity_id']] except Exception: raise ImproperlyConfigured( "No config for SP %s defined in SAML_IDP_SPCONFIG" % resp_args['sp_entity_id']) # Create user-specified processor or fallback to all-access base processor processor_string = sp_config.get('processor', None) if processor_string is None: processor = BaseProcessor else: processor_class = import_string(processor_string) processor = processor_class() # Check if user has access to the service of this SP if not processor.has_access(request.user): raise PermissionDenied("You do not have access to this resource") # Create Identity dict (SP-specific) sp_mapping = sp_config.get('attribute_mapping', {'username': '******'}) identity = processor.create_identity(request.user, sp_mapping) # TODO investigate how this works, because I don't get it. Specification? req_authn_context = req_info.message.requested_authn_context or PASSWORD AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(req_authn_context), "") # Construct SamlResponse message try: authn_resp = IDP.create_authn_response( identity=identity, userid=request.user.username, name_id=NameID(format=resp_args['name_id_policy'].format, sp_name_qualifier=destination, text=request.user.username), authn=AUTHN_BROKER.get_authn_by_accr(req_authn_context), sign_response=IDP.config.getattr("sign_response", "idp") or False, sign_assertion=IDP.config.getattr("sign_assertion", "idp") or False, **resp_args) except Exception as excp: return HttpResponseServerError(excp) # Return as html with self-submitting form. http_args = IDP.apply_binding(binding=binding_out, msg_str="%s" % authn_resp, destination=destination, relay_state=request.session['RelayState'], response=True) logger.debug('http args are: %s' % http_args) if processor.enable_multifactor(request.user): # Store http_args in session for after multi factor is complete request.session['saml_data'] = http_args['data'] logger.debug("Redirecting to process_multi_factor") return HttpResponseRedirect(reverse('saml_multi_factor')) else: logger.debug("Performing SAML redirect") return HttpResponse(http_args['data'])
from saml2.authn_context import PASSWORDPROTECTEDTRANSPORT from saml2.authn_context import UNSPECIFIED from saml2.authn_context import authn_context_class_ref from saml2.authn_context import requested_authn_context import eduid_idp.error SWAMID_AL1 = 'http://www.swamid.se/policy/assurance/al1' SWAMID_AL2 = 'http://www.swamid.se/policy/assurance/al2' SWAMID_AL3 = 'http://www.swamid.se/policy/assurance/al3' EDUID_INTERNAL_1_NAME = 'eduid.se:level:1' EDUID_INTERNAL_2_NAME = 'eduid.se:level:2' EDUID_INTERNAL_3_NAME = 'eduid.se:level:3' EDUID_INTERNAL_1 = authn_context_class_ref(EDUID_INTERNAL_1_NAME) EDUID_INTERNAL_2 = authn_context_class_ref(EDUID_INTERNAL_2_NAME) EDUID_INTERNAL_3 = authn_context_class_ref(EDUID_INTERNAL_3_NAME) EDUID_INTERNAL_UNSPECIFIED = authn_context_class_ref(UNSPECIFIED) # Default set of canonicalizations for authentication contexts _context_to_internal = { 'undefined': EDUID_INTERNAL_1, # the default entry, used on unknown RequestedAuthnContext # Level 1 SWAMID_AL1: EDUID_INTERNAL_1, PASSWORD: EDUID_INTERNAL_1, PASSWORDPROTECTEDTRANSPORT: EDUID_INTERNAL_1, UNSPECIFIED: EDUID_INTERNAL_1, # Level 2 SWAMID_AL2: EDUID_INTERNAL_2,
help="How long, in days, the metadata is valid from " "the time of creation") parser.add_argument('-c', dest='cert', help='certificate') parser.add_argument('-i', dest='id', help="The ID of the entities descriptor") parser.add_argument('-k', dest='keyfile', help="A file with a key to sign the metadata with") parser.add_argument('-n', dest='name') parser.add_argument('-s', dest='sign', action='store_true', help="sign the metadata") parser.add_argument('-m', dest='mako_root', default="./") parser.add_argument(dest="config") args = parser.parse_args() AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(PASSWORD), username_password_authn, 10, "http://%s" % socket.gethostname()) AUTHN_BROKER.add(authn_context_class_ref(UNSPECIFIED), "", 0, "http://%s" % socket.gethostname()) CONFIG = importlib.import_module(args.config) IDP = server.Server(args.config, cache=Cache()) IDP.ticket = {} _rot = args.mako_root LOOKUP = TemplateLookup(directories=[_rot + 'templates', _rot + 'htdocs'], module_directory=_rot + 'modules', input_encoding='utf-8', output_encoding='utf-8') HOST = CONFIG.HOST PORT = CONFIG.PORT
# allow uwsgi or gunicorn mount # by moving some initialization out of __name__ == '__main__' section. # uwsgi -s 0.0.0.0:8088 --protocol http --callable application --module idp args = type('Config', (object, ), {}) args.config = 'idp_conf' args.mako_root = './' args.path = None import socket from idp_user import USERS from idp_user import EXTRA from mako.lookup import TemplateLookup AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(PASSWORD), username_password_authn, 10, "http://%s" % socket.gethostname()) AUTHN_BROKER.add(authn_context_class_ref(UNSPECIFIED), "", 0, "http://%s" % socket.gethostname()) CONFIG = importlib.import_module(args.config) IDP = server.Server(args.config, cache=Cache()) IDP.ticket = {} # ---------------------------------------------------------------------------- if __name__ == '__main__': from wsgiref.simple_server import make_server parser = argparse.ArgumentParser() parser.add_argument('-p', dest='path', help='Path to configuration file.') parser.add_argument('-v',
def get(self, request, *args, **kwargs): # pylint: disable=missing-function-docstring, unused-argument, too-many-locals resp_args = { 'in_response_to': self.IN_RESPONSE_TO, 'sp_entity_id': self.SP_ENTITY_ID, 'name_id_policy': saml.NAMEID_FORMAT_PERSISTENT, 'binding': BINDING_HTTP_POST, 'destination': self.DESTINATION, } sp_config = { 'processor': 'djangosaml2idp.processors.BaseProcessor', 'attribute_mapping': { 'username': '******', 'token': 'token', 'aliyun_sso_roles': self.CUSTOM_CONFIG['role'], 'uid': self.CUSTOM_CONFIG['role_session_name'], 'aliyun_sso_session_duration': self.CUSTOM_CONFIG['session_duration'], }, } processor = self.get_processor(resp_args['sp_entity_id'], sp_config) # Check if user has access to the service of this SP if not processor.has_access(request): return self.handle_error( request, exception=PermissionDenied( "You do not have access to this resource"), status=403) cookie_user = self.cookie_user(request) if not cookie_user.aliyun_sso_role.is_active: # 用户的角色SSO被禁用 return self.handle_error( request, exception=PermissionDenied("Your role SSO has been disabled"), status=403) identity = self.get_identity(processor, cookie_user, sp_config) # print('identity is', identity) AUTHN_BROKER = AuthnBroker() # pylint: disable=invalid-name AUTHN_BROKER.add(authn_context_class_ref(PASSWORD), "") user_id = processor.get_user_id(cookie_user) # Construct SamlResponse message try: app = SAMLAPP.valid_objects.get( entity_id=resp_args['sp_entity_id']) _spsso_descriptor = entity_descriptor_from_string( app.xmldata).spsso_descriptor.pop() # pylint: disable=no-member authn_resp = self.IDP.create_authn_response( identity=identity, userid=user_id, name_id=NameID(format=resp_args['name_id_policy'], sp_name_qualifier=resp_args['sp_entity_id'], text=user_id), authn=AUTHN_BROKER.get_authn_by_accr(PASSWORD), sign_response=getattr(_spsso_descriptor, 'want_response_signed', '') == 'true', sign_assertion=getattr(_spsso_descriptor, 'want_assertions_signed', '') == 'true', **resp_args) except Exception as excp: # pylint: disable=broad-except return self.handle_error(request, exception=excp, status=500) # print('authn_resp is', authn_resp) http_args = self.IDP.apply_binding( binding=resp_args['binding'], msg_str="%s" % authn_resp, destination=resp_args['destination'], response=True) return HttpResponse(http_args['data'])
def main(): global IDP global AUTHN_BROKER global LOOKUP global args global CONFIG global USERS global PASSWD global EXTRA sys.path.insert(0, os.getcwd()) from wsgiref.simple_server import make_server parser = argparse.ArgumentParser() parser.add_argument('-p', dest='path', help='Path to configuration file.') parser.add_argument('-v', dest='valid', help="How long, in days, the metadata is valid from the time of creation") parser.add_argument('-c', dest='cert', help='certificate') parser.add_argument('-i', dest='id', help="The ID of the entities descriptor") parser.add_argument('-k', dest='keyfile', help="A file with a key to sign the metadata with") parser.add_argument('-n', dest='name') parser.add_argument('-s', dest='sign', action='store_true', help="sign the metadata") parser.add_argument('-m', dest='mako_root', default="./") parser.add_argument(dest="config") args = parser.parse_args() AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(PASSWORD), username_password_authn, 10, "http://%s" % socket.gethostname()) AUTHN_BROKER.add(authn_context_class_ref(UNSPECIFIED), "", 0, "http://%s" % socket.gethostname()) CONFIG = importlib.import_module(args.config) USERS = CONFIG.USERS PASSWD = CONFIG.PASSWD EXTRA = CONFIG.EXTRA IDP = server.Server(args.config, cache=Cache()) IDP.ticket = {} _rot = args.mako_root LOOKUP = TemplateLookup(directories=[_rot + 'htdocs', _rot + 'htdocs'], module_directory=_rot + 'modules', input_encoding='utf-8', output_encoding='utf-8') HOST = CONFIG.HOST PORT = CONFIG.PORT #SRV = make_server(HOST, PORT, application) SRV = wsgiserver.CherryPyWSGIServer(('0.0.0.0', PORT), application) make_server if CONFIG.HTTPS: SRV.ssl_adapter = ssl_pyopenssl.pyOpenSSLAdapter(CONFIG.SERVER_CERT, CONFIG.SERVER_KEY, CONFIG.CERT_CHAIN) print "IdP listening on %s:%s" % (HOST, PORT) try: SRV.start() except KeyboardInterrupt: SRV.stop()
def get(self, request, *args, **kwargs): # pylint: disable=missing-function-docstring, too-many-locals, unused-argument passed_data = request.POST if request.method == 'POST' else request.GET # get sp information from the parameters try: sp_entity_id = passed_data['sp'] except KeyError as excp: return self.handle_error(request, exception=excp, status=400) try: # sp_config = SAML_IDP_SPCONFIG[sp_entity_id] sp_config = { 'processor': 'djangosaml2idp.processors.BaseProcessor', 'attribute_mapping': { # DJANGO: SAML 'username': '******', 'email': 'email', 'name': 'first_name', 'is_boss': 'is_admin', 'token': 'token', } } except Exception: # pylint: disable=broad-except return self.handle_error( request, exception=ImproperlyConfigured( "No config for SP %s defined in SAML_IDP_SPCONFIG" % sp_entity_id), status=400) binding_out, destination = self.IDP.pick_binding( service="assertion_consumer_service", entity_id=sp_entity_id) processor = self.get_processor(sp_entity_id, sp_config) # Check if user has access to the service of this SP if not processor.has_access(request): return self.handle_error( request, exception=PermissionDenied( "You do not have access to this resource"), status=403) identity = self.get_identity(processor, request.user, sp_config) req_authn_context = PASSWORD AUTHN_BROKER = AuthnBroker() # pylint: disable=invalid-name AUTHN_BROKER.add(authn_context_class_ref(req_authn_context), "") user_id = processor.get_user_id(request.user) # Construct SamlResponse messages try: name_id_formats = self.IDP.config.getattr( "name_id_format", "idp") or [NAMEID_FORMAT_UNSPECIFIED] name_id = NameID(format=name_id_formats[0], text=user_id) authn = AUTHN_BROKER.get_authn_by_accr(req_authn_context) sign_response = self.IDP.config.getattr("sign_response", "idp") or False sign_assertion = self.IDP.config.getattr("sign_assertion", "idp") or False authn_resp = self.IDP.create_authn_response( identity=identity, in_response_to=None, destination=destination, sp_entity_id=sp_entity_id, userid=user_id, name_id=name_id, authn=authn, sign_response=sign_response, sign_assertion=sign_assertion, **passed_data) except Exception as excp: # pylint: disable=broad-except return self.handle_error(request, exception=excp, status=500) # Return as html with self-submitting form. http_args = self.IDP.apply_binding( binding=binding_out, msg_str="%s" % authn_resp, destination=destination, relay_state=passed_data['RelayState'], response=True) return HttpResponse(http_args['data'])
def get(self, request, *args, **kwargs): # pylint: disable=missing-function-docstring, unused-argument, too-many-locals binding = request.session.get('Binding', BINDING_HTTP_POST) # Parse incoming request try: req_info = self.IDP.parse_authn_request( request.session['SAMLRequest'], binding) except Exception as excp: # pylint: disable=broad-except return self.handle_error(request, exception=excp) # Signed request for HTTP-REDIRECT if "SigAlg" in request.session and "Signature" in request.session: _certs = self.IDP.metadata.certs(req_info.message.issuer.text, "any", "signing") verified_ok = False for cert in _certs: # pylint: disable=unused-variable # TODO implement # if verify_redirect_signature(_info, self.IDP.sec.sec_backend, cert): # verified_ok = True # break pass if not verified_ok: return self.handle_error( request, extra_message="Message signature verification failure", status=400) # Gather response arguments try: resp_args = self.IDP.response_args(req_info.message) except (UnknownPrincipal, UnsupportedBinding) as excp: return self.handle_error(request, exception=excp, status=400) try: # sp_config = SAML_IDP_SPCONFIG[resp_args['sp_entity_id']] sp_config = { 'processor': 'djangosaml2idp.processors.BaseProcessor', 'attribute_mapping': { # DJANGO: SAML 'email': 'email', 'private_email': 'private_email', 'username': '******', 'is_staff': 'is_staff', 'is_superuser': '******', 'token': 'token', }, } except Exception: # pylint: disable=broad-except return self.handle_error( request, exception=ImproperlyConfigured( "No config for SP %s defined in SAML_IDP_SPCONFIG" % resp_args['sp_entity_id']), status=400) processor = self.get_processor(resp_args['sp_entity_id'], sp_config) # Check if user has access to the service of this SP if not processor.has_access(request): return self.handle_error( request, exception=PermissionDenied( "You do not have access to this resource"), status=403) cookie_user = self.cookie_user(request) identity = self.get_identity(processor, cookie_user, sp_config) req_authn_context = req_info.message.requested_authn_context or PASSWORD AUTHN_BROKER = AuthnBroker() # pylint: disable=invalid-name AUTHN_BROKER.add(authn_context_class_ref(req_authn_context), "") user_id = processor.get_user_id(cookie_user) # Construct SamlResponse message try: authn_resp = self.IDP.create_authn_response( identity=identity, userid=user_id, name_id=NameID(format=resp_args['name_id_policy'].format, sp_name_qualifier=resp_args['sp_entity_id'], text=user_id), authn=AUTHN_BROKER.get_authn_by_accr(req_authn_context), sign_response=self.IDP.config.getattr("sign_response", "idp") or False, sign_assertion=self.IDP.config.getattr("sign_assertion", "idp") or False, **resp_args) except Exception as excp: # pylint: disable=broad-except return self.handle_error(request, exception=excp, status=500) http_args = self.IDP.apply_binding( binding=resp_args['binding'], msg_str="%s" % authn_resp, destination=resp_args['destination'], relay_state=request.session['RelayState'], response=True) logger.debug('http args are: %s' % http_args) # pylint: disable=logging-not-lazy return self.render_response(request, processor, http_args)
parser.add_argument('-k', dest='keyfile', help="A file with a key to sign the metadata with") parser.add_argument('-n', dest='name') parser.add_argument('-s', dest='sign', action='store_true', help="sign the metadata") parser.add_argument('-m', dest='mako_root', default="./") parser.add_argument(dest="config") args = parser.parse_args() CONFIG = importlib.import_module(args.config) AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(PASSWORD), username_password_authn, 10, CONFIG.BASE) AUTHN_BROKER.add(authn_context_class_ref(UNSPECIFIED), "", 0, CONFIG.BASE) IDP = server.Server(args.config, cache=Cache()) IDP.ticket = {} _rot = args.mako_root LOOKUP = TemplateLookup(directories=[_rot + 'templates', _rot + 'htdocs'], module_directory=_rot + 'modules', input_encoding='utf-8', output_encoding='utf-8') HOST = CONFIG.HOST PORT = CONFIG.PORT
from wsgiref.simple_server import make_server parser = argparse.ArgumentParser() parser.add_argument("-p", dest="path", help="Path to configuration file.") parser.add_argument("-v", dest="valid", help="How long, in days, the metadata is valid from the time of creation") parser.add_argument("-c", dest="cert", help="certificate") parser.add_argument("-i", dest="id", help="The ID of the entities descriptor") parser.add_argument("-k", dest="keyfile", help="A file with a key to sign the metadata with") parser.add_argument("-n", dest="name") parser.add_argument("-s", dest="sign", action="store_true", help="sign the metadata") parser.add_argument("-m", dest="mako_root", default="./") parser.add_argument(dest="config") args = parser.parse_args() AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(PASSWORD), username_password_authn, 10, "http://%s" % socket.gethostname()) AUTHN_BROKER.add(authn_context_class_ref(UNSPECIFIED), "", 0, "http://%s" % socket.gethostname()) CONFIG = importlib.import_module(args.config) IDP = server.Server(args.config, cache=Cache()) IDP.ticket = {} _rot = args.mako_root LOOKUP = TemplateLookup( directories=[_rot + "templates", _rot + "htdocs"], module_directory=_rot + "modules", input_encoding="utf-8", output_encoding="utf-8", ) HOST = CONFIG.HOST PORT = CONFIG.PORT
# allow uwsgi or gunicorn mount # by moving some initialization out of __name__ == '__main__' section. # uwsgi -s 0.0.0.0:8088 --protocol http --callable application --module idp args = type('Config', (object,), { }) args.config = 'idp_conf' args.mako_root = './' args.path = None import socket from idp_user import USERS from idp_user import EXTRA from mako.lookup import TemplateLookup AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(PASSWORD), username_password_authn, 10, "http://%s" % socket.gethostname()) AUTHN_BROKER.add(authn_context_class_ref(UNSPECIFIED), "", 0, "http://%s" % socket.gethostname()) IDP = server.Server(args.config, cache=Cache()) IDP.ticket = {} # ---------------------------------------------------------------------------- if __name__ == '__main__': from wsgiref.simple_server import make_server parser = argparse.ArgumentParser() parser.add_argument('-p', dest='path', help='Path to configuration file.')
def get(self, request, *args, **kwargs): # Parse incoming request try: req_info = self.IDP.parse_authn_request(request.session['SAMLRequest'], BINDING_HTTP_POST) except Exception as excp: return HttpResponseBadRequest(excp) # TODO this is taken from example, but no idea how this works or whats it does. Check SAML2 specification? # Signed request for HTTP-REDIRECT if "SigAlg" in request.session and "Signature" in request.session: _certs = self.IDP.metadata.certs(req_info.message.issuer.text, "any", "signing") verified_ok = False for cert in _certs: # TODO implement #if verify_redirect_signature(_info, self.IDP.sec.sec_backend, cert): # verified_ok = True # break pass if not verified_ok: return HttpResponseBadRequest("Message signature verification failure") binding_out, destination = self.IDP.pick_binding(service="assertion_consumer_service", entity_id=req_info.message.issuer.text) # Gather response arguments try: resp_args = self.IDP.response_args(req_info.message) except (UnknownPrincipal, UnsupportedBinding) as excp: return HttpResponseServerError(excp) try: sp_config = settings.SAML_IDP_SPCONFIG[resp_args['sp_entity_id']] except Exception: raise ImproperlyConfigured("No config for SP %s defined in SAML_IDP_SPCONFIG" % resp_args['sp_entity_id']) processor = self.get_processor(sp_config) # Check if user has access to the service of this SP if not processor.has_access(request.user): raise PermissionDenied("You do not have access to this resource") identity = self.get_identity(processor, request.user, sp_config) # TODO investigate how this works, because I don't get it. Specification? req_authn_context = req_info.message.requested_authn_context or PASSWORD AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(authn_context_class_ref(req_authn_context), "") # Construct SamlResponse message try: authn_resp = self.IDP.create_authn_response( identity=identity, userid=request.user.username, name_id=NameID(format=resp_args['name_id_policy'].format, sp_name_qualifier=destination, text=request.user.username), authn=AUTHN_BROKER.get_authn_by_accr(req_authn_context), sign_response=self.IDP.config.getattr("sign_response", "idp") or False, sign_assertion=self.IDP.config.getattr("sign_assertion", "idp") or False, **resp_args) except Exception as excp: return HttpResponseServerError(excp) http_args = self.IDP.apply_binding( binding=binding_out, msg_str="%s" % authn_resp, destination=destination, relay_state=request.session['RelayState'], response=True) logger.debug('http args are: %s' % http_args) return self.render_response(request, processor, http_args)