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 _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 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 test_authn_2(): authn = AuthnBroker() target = "https://example.org/login" authn.add(AUTHNCTXT, target, 10, "https://example.org") result = authn.pick(REQAUTHNCTXT) assert len(result) == 1 method, reference = result[0] assert target == method
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 init_AuthnBroker(my_id): """ Create and return a saml2 AuthnBroker. :param my_id: The IdP entity id as string :return: AuthnBroker instance :type my_id: string :rtype: AuthnBroker """ # NOTE: The function pointers supplied to the AUTHN_BROKER is not for authentication, # but for displaying proper login forms it seems. In eduid_idp, a single function # is used to display login screen regardless of Authn method, so a simple '1' is used # instead of the function pointer (has to evaulate to true). AUTHN_BROKER = AuthnBroker() AUTHN_BROKER.add(EDUID_INTERNAL_3, 1, 300, my_id, reference=EDUID_INTERNAL_3_NAME + ':300') AUTHN_BROKER.add(EDUID_INTERNAL_2, 1, 200, my_id, reference=EDUID_INTERNAL_2_NAME + ':200') AUTHN_BROKER.add(EDUID_INTERNAL_1, 1, 100, my_id, reference=EDUID_INTERNAL_1_NAME + ':100') AUTHN_BROKER.add(EDUID_INTERNAL_UNSPECIFIED, "", 0, my_id, reference="eduid.se:level:unspecified") return AUTHN_BROKER
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() 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
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'])
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)
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)
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'])