コード例 #1
0
    def authn_response(self, context, binding):
        """
        Endpoint for the idp response
        :type context: satosa.context,Context
        :type binding: str
        :rtype: satosa.response.Response

        :param context: The current context
        :param binding: The saml binding type
        :return: response
        """
        if not context.request["SAMLResponse"]:
            msg = "Missing Response for state"
            logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state),
                                        message=msg)
            logger.debug(logline)
            raise SATOSAAuthenticationError(context.state, "Missing Response")

        try:
            authn_response = self.sp.parse_authn_request_response(
                context.request["SAMLResponse"],
                binding,
                outstanding=self.outstanding_queries)
        except Exception as err:
            msg = "Failed to parse authn request for state"
            logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state),
                                        message=msg)
            logger.debug(logline, exc_info=True)
            raise SATOSAAuthenticationError(
                context.state, "Failed to parse authn request") from err

        if self.sp.config.getattr('allow_unsolicited', 'sp') is False:
            req_id = authn_response.in_response_to
            if req_id not in self.outstanding_queries:
                msg = "No request with id: {}".format(req_id),
                logline = lu.LOG_FMT.format(id=lu.get_session_id(
                    context.state),
                                            message=msg)
                logger.debug(logline)
                raise SATOSAAuthenticationError(context.state, msg)
            del self.outstanding_queries[req_id]

        # check if the relay_state matches the cookie state
        if context.state[
                self.name]["relay_state"] != context.request["RelayState"]:
            msg = "State did not match relay state for state"
            logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state),
                                        message=msg)
            logger.debug(logline)
            raise SATOSAAuthenticationError(context.state,
                                            "State did not match relay state")

        context.decorate(Context.KEY_METADATA_STORE, self.sp.metadata)
        if self.config.get(SAMLBackend.KEY_MEMORIZE_IDP):
            issuer = authn_response.response.issuer.text.strip()
            context.state[Context.KEY_MEMORIZED_IDP] = issuer
        context.state.pop(self.name, None)
        context.state.pop(Context.KEY_FORCE_AUTHN, None)
        return self.auth_callback_func(
            context, self._translate_response(authn_response, context.state))
コード例 #2
0
    def authn_request(self, context, entity_id):
        """
        Do an authorization request on idp with given entity id.
        This is the start of the authorization.

        :type context: satosa.context.Context
        :type entity_id: str
        :rtype: satosa.response.Response

        :param context: The current context
        :param entity_id: Target IDP entity id
        :return: response to the user agent
        """

        # If IDP blacklisting is enabled and the selected IDP is blacklisted,
        # stop here
        if self.idp_blacklist_file:
            with open(self.idp_blacklist_file) as blacklist_file:
                blacklist_array = json.load(blacklist_file)['blacklist']
                if entity_id in blacklist_array:
                    satosa_logging(logger, logging.DEBUG, "IdP with EntityID {} is blacklisted".format(entity_id), context.state, exc_info=False)
                    raise SATOSAAuthenticationError(context.state, "Selected IdP is blacklisted for this backend")

        kwargs = {}
        authn_context = self.construct_requested_authn_context(entity_id)
        if authn_context:
            kwargs['requested_authn_context'] = authn_context

        try:
            binding, destination = self.sp.pick_binding(
                "single_sign_on_service", None, "idpsso", entity_id=entity_id)
            satosa_logging(logger, logging.DEBUG, "binding: %s, destination: %s" % (binding, destination),
                           context.state)
            acs_endp, response_binding = self.sp.config.getattr("endpoints", "sp")["assertion_consumer_service"][0]
            req_id, req = self.sp.create_authn_request(
                destination, binding=response_binding, **kwargs)
            relay_state = util.rndstr()
            ht_args = self.sp.apply_binding(binding, "%s" % req, destination, relay_state=relay_state)
            satosa_logging(logger, logging.DEBUG, "ht_args: %s" % ht_args, context.state)
        except Exception as exc:
            satosa_logging(logger, logging.DEBUG, "Failed to construct the AuthnRequest for state", context.state,
                           exc_info=True)
            raise SATOSAAuthenticationError(context.state, "Failed to construct the AuthnRequest") from exc

        if self.sp.config.getattr('allow_unsolicited', 'sp') is False:
            if req_id in self.outstanding_queries:
                errmsg = "Request with duplicate id {}".format(req_id)
                satosa_logging(logger, logging.DEBUG, errmsg, context.state)
                raise SATOSAAuthenticationError(context.state, errmsg)
            self.outstanding_queries[req_id] = req

        context.state[self.name] = {"relay_state": relay_state}
        return make_saml_response(binding, ht_args)
コード例 #3
0
ファイル: saml2.py プロジェクト: trsau/SATOSA
    def authn_response(self, context, binding):
        """
        Endpoint for the idp response
        :type context: satosa.context,Context
        :type binding: str
        :rtype: satosa.response.Response

        :param context: The current context
        :param binding: The saml binding type
        :return: response
        """
        if not context.request["SAMLResponse"]:
            satosa_logging(logger, logging.DEBUG, "Missing Response for state",
                           context.state)
            raise SATOSAAuthenticationError(context.state, "Missing Response")

        try:
            authn_response = self.sp.parse_authn_request_response(
                context.request["SAMLResponse"],
                binding,
                outstanding=self.outstanding_queries)
        except Exception as err:
            satosa_logging(logger,
                           logging.DEBUG,
                           "Failed to parse authn request for state",
                           context.state,
                           exc_info=True)
            raise SATOSAAuthenticationError(
                context.state, "Failed to parse authn request") from err

        if self.sp.config.getattr('allow_unsolicited', 'sp') is False:
            req_id = authn_response.in_response_to
            if req_id not in self.outstanding_queries:
                errmsg = "No request with id: {}".format(req_id),
                satosa_logging(logger, logging.DEBUG, errmsg, context.state)
                raise SATOSAAuthenticationError(context.state, errmsg)
            del self.outstanding_queries[req_id]

        # check if the relay_state matches the cookie state
        if context.state[
                self.name]["relay_state"] != context.request["RelayState"]:
            satosa_logging(logger, logging.DEBUG,
                           "State did not match relay state for state",
                           context.state)
            raise SATOSAAuthenticationError(context.state,
                                            "State did not match relay state")

        context.decorate(Context.KEY_BACKEND_METADATA_STORE, self.sp.metadata)

        del context.state[self.name]
        return self.auth_callback_func(
            context, self._translate_response(authn_response, context.state))
コード例 #4
0
ファイル: inacademia_backend.py プロジェクト: SUNET/svs
    def _translate_response(self, auth_response, state):
        if 'eduPersonAffiliation' not in auth_response.ava:
            raise SATOSAAuthenticationError(
                state, 'Missing eduPersonAffiliation in response from IdP.')

        internal_resp = super()._translate_response(auth_response, state)

        internal_resp.user_id = self._get_user_id(auth_response)
        if not internal_resp.user_id:
            raise SATOSAAuthenticationError(
                state,
                'Failed to construct persistent user id from IdP response.')

        return internal_resp
コード例 #5
0
ファイル: oauth.py プロジェクト: borgand/SATOSA
    def verify_state(self, resp, state_data, state):
        """
        Will verify the state and throw and error if the state is invalid.
        :type resp: AuthorizationResponse
        :type state_data: dict[str, str]
        :type state: satosa.state.State

        :param resp: The authorization response from the OP, created by pyoidc.
        :param state_data: The state data for this backend.
        :param state: The current state for the proxy and this backend.
        Only used for raising errors.
        """
        if not ("state" in resp and "state" in state_data
                and resp["state"] == state_data["state"]):
            tmp_state = ""
            if "state" in resp:
                tmp_state = resp["state"]
            satosa_logging(LOGGER,
                           logging.DEBUG,
                           "Missing or invalid state [%s] in response!" %
                           tmp_state,
                           state,
                           exc_info=True)
            raise SATOSAAuthenticationError(
                state,
                "Missing or invalid state [%s] in response!" % tmp_state)
コード例 #6
0
    def _translate_response(self, auth_response, state):
        # translate() will handle potentially encrypted SAML Assertions
        # auth_response object will also be modified
        # import pdb; pdb.set_trace()
        internal_resp = super()._translate_response(auth_response, state)
        if not any(
                affiliation_attr in auth_response.ava
                for affiliation_attr in self.config['affiliation_attributes']):
            raise SATOSAProcessingHaltError(
                state=state,
                message="No affiliation attribute from IdP",
                redirect_uri=self.error_uri)

        params = parse_qs(state['InAcademia']['oidc_request'])
        if 'persistent' in params['scope'][0].split(" "):
            scope = 'persistent'
        else:
            scope = 'transient'
        internal_resp.user_id = self._get_user_id(auth_response, scope)
        if not internal_resp.user_id:
            raise SATOSAAuthenticationError(
                state,
                'Failed to construct persistent user id from IdP response.')

        return internal_resp
コード例 #7
0
ファイル: saml2.py プロジェクト: nsklikas/SATOSA
    def disco_response(self, context):
        """
        Endpoint for the discovery server response

        :type context: satosa.context.Context
        :rtype: satosa.response.Response

        :param context: The current context
        :return: response
        """
        info = context.request
        state = context.state
        requested_attributes = state.pop(Context.KEY_REQUESTED_ATTRIBUTES,
                                         None)

        try:
            entity_id = info["entityID"]
        except KeyError as err:
            msg = "No IDP chosen for state"
            logline = lu.LOG_FMT.format(id=lu.get_session_id(state),
                                        message=msg)
            logger.debug(logline, exc_info=True)
            raise SATOSAAuthenticationError(state, "No IDP chosen") from err

        return self.authn_request(context,
                                  entity_id,
                                  requested_attributes=requested_attributes)
コード例 #8
0
ファイル: saml2.py プロジェクト: borgand/SATOSA
    def disco_response(self, context):
        """
        Endpoint for the discovery server response

        :type context: satosa.context.Context
        :rtype: satosa.response.Response

        :param context: The current context
        :return: response
        """
        info = context.request

        state = context.state

        try:
            entity_id = info[self.idp_disco_query_param]
        except KeyError as err:
            satosa_logging(LOGGER,
                           logging.DEBUG,
                           "No IDP chosen for state",
                           state,
                           exc_info=True)
            raise SATOSAAuthenticationError(state, "No IDP chosen") from err
        else:
            request_info = InternalRequest(None, None)
            return self.authn_request(context, entity_id, request_info)
コード例 #9
0
ファイル: openid_connect.py プロジェクト: borgand/SATOSA
    def redirect_endpoint(self, context, *args):
        """
        Handles the authentication response from the OP.
        :type context: satosa.context.Context
        :type args: Any
        :rtype: satosa.response.Response

        :param context: SATOSA context
        :param args: None
        :return:
        """
        state = context.state
        backend_state = state.get(self.config.STATE_ID)
        if backend_state["state"] != context.request["state"]:
            satosa_logging(
                LOGGER, logging.DEBUG,
                "Missing or invalid state in authn response for state: %s" %
                backend_state, state)
            raise SATOSAAuthenticationError(
                state, "Missing or invalid state in authn response")
        client = self.restore_state(backend_state)
        result = client.callback(context.request, state, backend_state)
        context.state.remove(self.config.STATE_ID)
        return self.auth_callback_func(
            context,
            self._translate_response(
                result,
                client.authorization_endpoint,
                self.get_subject_type(client),
            ))
コード例 #10
0
    def _get_uuid(self, context, issuer, id):
        """
        Ask the account linking service for a uuid.
        If the given issuer/id pair is not linked, then the function will return a ticket.
        This ticket should be used for linking the issuer/id pair to the user account

        :type context: satosa.context.Context
        :type issuer: str
        :type id: str
        :rtype: (int, str)

        :param context: The current context
        :param issuer: the issuer used for authentication
        :param id: the given id
        :return: response status code and message
            (200, uuid) or (400, ticket)
        """
        data = {
            "idp":
            issuer,
            "id":
            id,
            "redirect_endpoint":
            "%s/account_linking/%s" % (self.proxy_base, self.endpoint)
        }
        jws = self._to_jws(data)

        try:
            request = "{}/get_id?jwt={}".format(self.al_rest_uri, jws)
            response = requests.get(request, verify=self.verify_ssl)
        except requests.ConnectionError as con_exc:
            msg = "Could not connect to account linking service"
            satosa_logging(LOGGER,
                           logging.CRITICAL,
                           msg,
                           context.state,
                           exc_info=True)
            raise SATOSAAuthenticationError(context.state, msg) from con_exc

        if response.status_code != 200 and response.status_code != 404:
            msg = "Got status code '%s' from account linking service" % (
                response.status_code)
            satosa_logging(LOGGER, logging.CRITICAL, msg, context.state)
            raise SATOSAAuthenticationError(context.state, msg)

        return response.status_code, response.text
コード例 #11
0
ファイル: spidsaml2.py プロジェクト: berez23/Satosa-Saml2Spid
 def check_blacklist(self):
     # If IDP blacklisting is enabled and the selected IDP is blacklisted,
     # stop here
     if self.idp_blacklist_file:
         with open(self.idp_blacklist_file) as blacklist_file:
             blacklist_array = json.load(blacklist_file)['blacklist']
             if entity_id in blacklist_array:
                 logger.debug("IdP with EntityID {} is blacklisted".format(entity_id))
                 raise SATOSAAuthenticationError(context.state, "Selected IdP is blacklisted for this backend")
コード例 #12
0
    def deny_consent(self, context):
        """
        Endpoint for handling denied consent.
        :type context: satosa.context.Context
        :rtype: satosa.response.Response

        :param context: response context
        :return: response
        """
        del context.state[STATE_KEY]
        transaction_log(context.state,
                        self.config.get("consent_exit_order", 1010),
                        "user_consent", "deny", "exit", "cancel", '', '',
                        ErrorDescription.USER_CONSENT_DENIED[LOG_MSG], 'user')

        auth_error = SATOSAAuthenticationError(context.state, '')
        auth_error._message = ErrorDescription.USER_CONSENT_DENIED[ERROR_DESC]
        raise auth_error
コード例 #13
0
ファイル: oauth.py プロジェクト: borgand/SATOSA
    def authn_response(self, context):
        """
        Handles the authentication response from the OP.

        :type context: satosa.context.Context
        :rtype: satosa.response.Response
        :param context: The context in SATOSA
        :return: A SATOSA response. This method is only responsible to call the callback function
        which generates the Response object.
        """
        state = context.state
        try:
            state_data = state.get(self.config["state_id"])
            consumer = self.get_consumer()
            request = context.request
            aresp = consumer.parse_response(AuthorizationResponse,
                                            info=json.dumps(request))
            self.verify_state(aresp, state_data, state)
            rargs = {
                "code": aresp["code"],
                "redirect_uri": self.redirect_url,
                "state": state_data["state"]
            }
            atresp = consumer.do_access_token_request(request_args=rargs,
                                                      state=aresp["state"])
            if ("verify_accesstoken_state" not in self.config
                    or self.config["verify_accesstoken_state"]):
                self.verify_state(atresp, state_data, state)
            user_info = self.user_information(atresp["access_token"])
            internal_response = InternalResponse(
                auth_info=self.auth_info(request))
            internal_response.add_attributes(
                self.converter.to_internal(self.external_type, user_info))
            internal_response.set_user_id(user_info[self.user_id_attr])
            if "user_id_params" in self.config:
                user_id = ""
                for param in self.config["user_id_params"]:
                    try:
                        user_id += user_info[param]
                    except Exception as error:
                        raise SATOSAAuthenticationError from error
                internal_response.set_user_id(user_id)
            context.state.remove(self.config["state_id"])
            return self.auth_callback_func(context, internal_response)
        except Exception as error:
            satosa_logging(LOGGER,
                           logging.DEBUG,
                           "Not a valid authentication",
                           state,
                           exc_info=True)
            if isinstance(error, SATOSAError):
                raise error
            if state is not None:
                raise SATOSAAuthenticationError(
                    state, "Not a valid authentication") from error
            raise
コード例 #14
0
ファイル: saml2.py プロジェクト: borgand/SATOSA
    def authn_response(self, context, binding):
        """
        Endpoint for the idp response
        :type context: satosa.context,Context
        :type binding: str
        :rtype: satosa.response.Response

        :param context: The current context
        :param binding: The saml binding type
        :return: response
        """
        _authn_response = context.request

        state = context.state

        if not _authn_response["SAMLResponse"]:
            satosa_logging(LOGGER, logging.DEBUG, "Missing Response for state",
                           state)
            raise SATOSAAuthenticationError(state, "Missing Response")

        try:
            _response = self.sp.parse_authn_request_response(
                _authn_response["SAMLResponse"], binding)
        except Exception as err:
            satosa_logging(LOGGER,
                           logging.DEBUG,
                           "Failed to parse authn request for state",
                           state,
                           exc_info=True)
            raise SATOSAAuthenticationError(
                state, "Failed to parse authn request") from err

        # check if the relay_state matches the cookie state
        if state.get(self.state_id) != _authn_response['RelayState']:
            satosa_logging(LOGGER, logging.DEBUG,
                           "State did not match relay state for state", state)
            raise SATOSAAuthenticationError(state,
                                            "State did not match relay state")

        context.state.remove(self.state_id)
        return self.auth_callback_func(
            context, self._translate_response(_response, context.state))
コード例 #15
0
ファイル: user_consent.py プロジェクト: SUNET/svs
    def deny_consent(self, context):
        """
        Endpoint for handling denied consent.
        :type context: satosa.context.Context
        :rtype: satosa.response.Response

        :param context: response context
        :return: response
        """
        del context.state[self.name]
        raise SATOSAAuthenticationError(context.state, 'Consent was denied by the user.')
コード例 #16
0
    def test_handle_backend_error(self, context, frontend):
        redirect_uri = "https://client.example.com"
        areq = AuthorizationRequest(client_id=CLIENT_ID, scope="openid", response_type="id_token",
                                    redirect_uri=redirect_uri)
        context.state[frontend.name] = {"oidc_request": areq.to_urlencoded()}

        # fake an error
        message = "test error"
        error = SATOSAAuthenticationError(context.state, message)
        resp = frontend.handle_backend_error(error)

        assert resp.message.startswith(redirect_uri)
        error_response = AuthorizationErrorResponse().deserialize(urlparse(resp.message).fragment)
        error_response["error"] = "access_denied"
        error_response["error_description"] == message
コード例 #17
0
    def disco_response(self, context):
        """
        Endpoint for the discovery server response

        :type context: satosa.context.Context
        :rtype: satosa.response.Response

        :param context: The current context
        :return: response
        """
        info = context.request
        state = context.state

        try:
            entity_id = info["entityID"]
        except KeyError as err:
            satosa_logging(logger, logging.DEBUG, "No IDP chosen for state", state, exc_info=True)
            raise SATOSAAuthenticationError(state, "No IDP chosen") from err

        return self.authn_request(context, entity_id)
コード例 #18
0
    def _translate_response(self, auth_response, state):
        # translate() will handle potentially encrypted SAML Assertions
        # auth_response object will also be modified
        # import pdb; pdb.set_trace()
        internal_resp = super()._translate_response(auth_response, state)
        satosa_logging(
            logger, logging.INFO, "Attributes received from IdP {} {}".format(
                auth_response.response.issuer.text,
                json.dumps([k for k in auth_response.ava.keys()])), state)
        resp_idp_entityid = internal_resp.to_dict().get('auth_info').get(
            'issuer')

        if not any(
                affiliation_attr in auth_response.ava
                for affiliation_attr in self.config['affiliation_attributes']):

            transaction_log(state, self.config.get("response_exit_order",
                                                   610), "inacademia_backend",
                            "response", "exit", "fail", '', resp_idp_entityid,
                            ErrorDescription.NO_AFFILIATION_ATTR[LOG_MSG],
                            'idp')

            auth_error = SATOSAAuthenticationError(state, "")
            auth_error._message = ErrorDescription.NO_AFFILIATION_ATTR[
                ERROR_DESC]
            raise auth_error

        params = parse_qs(state['InAcademia']['oidc_request'])
        if 'persistent' in params['scope'][0].split(" "):
            scope = 'persistent'
        else:
            scope = 'transient'
        internal_resp.user_id = self._get_user_id(auth_response, scope)
        if not internal_resp.user_id:

            transaction_log(
                state, self.config.get("response_exit_order",
                                       620), "inacademia_backend", "response",
                "exit", "fail", '', resp_idp_entityid, ErrorDescription.
                FAILED_TO_CONSTRUCT_PERSISTENT_USERID[LOG_MSG], 'idp')

            auth_error = SATOSAAuthenticationError(state, "")
            auth_error._message = ErrorDescription.FAILED_TO_CONSTRUCT_PERSISTENT_USERID[
                ERROR_DESC]
            raise auth_error

        return internal_resp
コード例 #19
0
ファイル: oauth.py プロジェクト: sgomez/SATOSA
    def _verify_state(self, resp, state_data, state):
        """
        Will verify the state and throw and error if the state is invalid.
        :type resp: AuthorizationResponse
        :type state_data: dict[str, str]
        :type state: satosa.state.State

        :param resp: The authorization response from the AS, created by pyoidc.
        :param state_data: The state data for this backend.
        :param state: The current state for the proxy and this backend.
        Only used for raising errors.
        """
        is_known_state = "state" in resp and "state" in state_data and resp[
            "state"] == state_data["state"]
        if not is_known_state:
            received_state = resp.get("state", "")
            satosa_logging(
                logger, logging.DEBUG,
                "Missing or invalid state [%s] in response!" % received_state,
                state)
            raise SATOSAAuthenticationError(
                state,
                "Missing or invalid state [%s] in response!" % received_state)
コード例 #20
0
ファイル: service_base.py プロジェクト: borgand/SATOSA
    def process_service_queue(self, context, data):
        """
        Processes the whole queue of micro services

        :type context: satosa.context.Context
        :type data: satosa.internal_data.InternalResponse | satosa.internal_data.InternalRequest
        :rtype: satosa.internal_data.InternalResponse | satosa.internal_data.InternalRequest

        :param context: The current context
        :param data: Data to be modified
        :return: Modified data
        """
        if self._child_service:
            data = self._child_service.process_service_queue(context, data)
        try:
            return self.process(context, data)
        except Exception as err:
            satosa_logging(LOGGER,
                           logging.DEBUG,
                           "Micro service error.",
                           context.state,
                           exc_info=True)
            raise SATOSAAuthenticationError(context.state,
                                            "Micro service error") from err
コード例 #21
0
ファイル: oauth.py プロジェクト: vyskocilpavel/SATOSA
    def _verify_state(self, resp, state_data, state):
        """
        Will verify the state and throw and error if the state is invalid.
        :type resp: AuthorizationResponse
        :type state_data: dict[str, str]
        :type state: satosa.state.State

        :param resp: The authorization response from the AS, created by pyoidc.
        :param state_data: The state data for this backend.
        :param state: The current state for the proxy and this backend.
        Only used for raising errors.
        """
        is_known_state = "state" in resp and "state" in state_data and resp[
            "state"] == state_data["state"]
        if not is_known_state:
            received_state = resp.get("state", "")
            msg = "Missing or invalid state [{}] in response!".format(
                received_state)
            logline = lu.LOG_FMT.format(id=lu.get_session_id(state),
                                        message=msg)
            logger.debug(logline)
            raise SATOSAAuthenticationError(
                state,
                "Missing or invalid state [%s] in response!" % received_state)
コード例 #22
0
ファイル: saml2.py プロジェクト: borgand/SATOSA
    def authn_request(self, context, entity_id, internal_req):
        """
        Do an authorization request on idp with given entity id.
        This is the start of the authorization.

        :type context: satosa.context.Context
        :type entity_id: str
        :type internal_req: satosa.internal_data.InternalRequest
        :rtype: satosa.response.Response

        :param context: The curretn context
        :param entity_id: Target IDP entity id
        :param internal_req: The request
        :return: Response
        """
        _cli = self.sp
        hash_type = UserIdHashType.persistent.name
        if "hash_type" in self.config:
            hash_type = self.config["hash_type"]
        req_args = {"name_id_policy": self.create_name_id_policy(hash_type)}

        state = context.state

        try:
            # Picks a binding to use for sending the Request to the IDP
            _binding, destination = _cli.pick_binding("single_sign_on_service",
                                                      self.bindings,
                                                      "idpsso",
                                                      entity_id=entity_id)
            satosa_logging(
                LOGGER, logging.DEBUG,
                "binding: %s, destination: %s" % (_binding, destination),
                state)
            # Binding here is the response binding that is which binding the
            # IDP should use to return the response.
            acs = _cli.config.getattr("endpoints",
                                      "sp")["assertion_consumer_service"]
            # just pick one
            endp, return_binding = acs[0]
            req_id, req = _cli.create_authn_request(destination,
                                                    binding=return_binding,
                                                    **req_args)
            relay_state = rndstr()
            ht_args = _cli.apply_binding(_binding,
                                         "%s" % req,
                                         destination,
                                         relay_state=relay_state)
            satosa_logging(LOGGER, logging.DEBUG, "ht_args: %s" % ht_args,
                           state)
        except Exception as exc:
            satosa_logging(LOGGER,
                           logging.DEBUG,
                           "Failed to construct the AuthnRequest for state",
                           state,
                           exc_info=True)
            raise SATOSAAuthenticationError(
                state, "Failed to construct the AuthnRequest") from exc

        state.add(self.state_id, relay_state)

        if _binding == BINDING_HTTP_REDIRECT:
            for param, value in ht_args["headers"]:
                if param == "Location":
                    resp = SeeOther(str(value))
                    break
            else:
                satosa_logging(LOGGER, logging.DEBUG,
                               "Parameter error for state", state)
                raise SATOSAAuthenticationError(state, "Parameter error")
        else:
            resp = Response(ht_args["data"], headers=ht_args["headers"])

        return resp
コード例 #23
0
    def authn_request(self, context, entity_id):
        """
        Do an authorization request on idp with given entity id.
        This is the start of the authorization.

        :type context: satosa.context.Context
        :type entity_id: str
        :rtype: satosa.response.Response

        :param context: The current context
        :param entity_id: Target IDP entity id
        :return: response to the user agent
        """

        # If IDP blacklisting is enabled and the selected IDP is blacklisted,
        # stop here
        if self.idp_blacklist_file:
            with open(self.idp_blacklist_file) as blacklist_file:
                blacklist_array = json.load(blacklist_file)['blacklist']
                if entity_id in blacklist_array:
                    msg = "IdP with EntityID {} is blacklisted".format(entity_id)
                    logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg)
                    logger.debug(logline, exc_info=False)
                    raise SATOSAAuthenticationError(context.state, "Selected IdP is blacklisted for this backend")

        kwargs = {}
        target_accr = context.state.get(Context.KEY_TARGET_AUTHN_CONTEXT_CLASS_REF)
        authn_context = self.construct_requested_authn_context(entity_id, target_accr=target_accr)
        if authn_context:
            kwargs["requested_authn_context"] = authn_context
        if self.config.get(SAMLBackend.KEY_MIRROR_FORCE_AUTHN):
            kwargs["force_authn"] = get_force_authn(
                context, self.config, self.sp.config
            )
        if self.config.get(SAMLBackend.KEY_SEND_REQUESTER_ID):
            requester = context.state.state_dict[STATE_KEY_BASE]['requester']
            kwargs["scoping"] = Scoping(requester_id=[RequesterID(text=requester)])

        try:
            acs_endp, response_binding = self.sp.config.getattr("endpoints", "sp")["assertion_consumer_service"][0]
            relay_state = util.rndstr()
            req_id, binding, http_info = self.sp.prepare_for_negotiated_authenticate(
                entityid=entity_id,
                response_binding=response_binding,
                relay_state=relay_state,
                **kwargs,
            )
        except Exception as e:
            msg = "Failed to construct the AuthnRequest for state"
            logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg)
            logger.debug(logline, exc_info=True)
            raise SATOSAAuthenticationError(context.state, "Failed to construct the AuthnRequest") from e

        if self.sp.config.getattr('allow_unsolicited', 'sp') is False:
            if req_id in self.outstanding_queries:
                msg = "Request with duplicate id {}".format(req_id)
                logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state), message=msg)
                logger.debug(logline)
                raise SATOSAAuthenticationError(context.state, msg)
            self.outstanding_queries[req_id] = req_id

        context.state[self.name] = {"relay_state": relay_state}
        return make_saml_response(binding, http_info)
コード例 #24
0
ファイル: spidsaml2.py プロジェクト: italia/Satosa-Saml2Spid
    def authn_response(self, context, binding):
        """
        Endpoint for the idp response
        :type context: satosa.context,Context
        :type binding: str
        :rtype: satosa.response.Response

        :param context: The current context
        :param binding: The saml binding type
        :return: response
        """
        if not context.request["SAMLResponse"]:
            logger.debug("Missing Response for state")
            raise SATOSAAuthenticationError(context.state, "Missing Response")

        try:
            authn_response = self.sp.parse_authn_request_response(
                context.request["SAMLResponse"],
                binding,
                outstanding=self.outstanding_queries,
            )
        except StatusAuthnFailed as err:
            erdict = re.search(r"ErrorCode nr(?P<err_code>\d+)", str(err))
            if erdict:
                return self.handle_spid_anomaly(erdict.groupdict()["err_code"],
                                                err)
            else:
                return self.handle_error(
                    **{
                        "err":
                        err,
                        "message":
                        "Autenticazione fallita",
                        "troubleshoot":
                        ("Anomalia riscontrata durante la fase di Autenticazione. "
                         f"{_TROUBLESHOOT_MSG}"),
                    })
        except SignatureError as err:
            return self.handle_error(
                **{
                    "err":
                    err,
                    "message":
                    "Autenticazione fallita",
                    "troubleshoot": (
                        "La firma digitale della risposta ottenuta "
                        f"non risulta essere corretta. {_TROUBLESHOOT_MSG}"),
                })
        except Exception as err:
            return self.handle_error(
                **{
                    "err": err,
                    "message":
                    "Anomalia riscontrata nel processo di Autenticazione",
                    "troubleshoot": _TROUBLESHOOT_MSG,
                })

        if self.sp.config.getattr("allow_unsolicited", "sp") is False:
            req_id = authn_response.in_response_to
            if req_id not in self.outstanding_queries:
                errmsg = ("No request with id: {}".format(req_id), )
                logger.debug(errmsg)
                return self.handle_error(**{
                    "message": errmsg,
                    "troubleshoot": _TROUBLESHOOT_MSG
                })
            del self.outstanding_queries[req_id]

        # Context validation
        if not context.state.get(self.name):
            _msg = f"context.state[self.name] KeyError: where self.name is {self.name}"
            logger.error(_msg)
            return self.handle_error(**{
                "message": _msg,
                "troubleshoot": _TROUBLESHOOT_MSG
            })
        # check if the relay_state matches the cookie state
        if context.state[
                self.name]["relay_state"] != context.request["RelayState"]:
            _msg = "State did not match relay state for state"
            return self.handle_error(**{
                "message": _msg,
                "troubleshoot": _TROUBLESHOOT_MSG
            })

        # Spid and SAML2 additional tests
        _sp_config = self.config["sp_config"]
        accepted_time_diff = _sp_config["accepted_time_diff"]
        recipient = _sp_config["service"]["sp"]["endpoints"][
            "assertion_consumer_service"][0][0]
        authn_context_classref = self.config["acr_mapping"][""]

        issuer = authn_response.response.issuer

        # this will get the entity name in state
        if len(context.state.keys()) < 2:
            _msg = "Inconsistent context.state"
            return self.handle_error(**{
                "message": _msg,
                "troubleshoot": _TROUBLESHOOT_MSG
            })

        list(context.state.keys())[1]
        # deprecated
        # if not context.state.get('Saml2IDP'):
        # _msg = "context.state['Saml2IDP'] KeyError"
        # logger.error(_msg)
        # raise SATOSAStateError(context.state, "State without Saml2IDP")
        in_response_to = context.state["req_args"]["id"]

        # some debug
        if authn_response.ava:
            logging.debug(f"Attributes to {authn_response.return_addrs} "
                          f"in_response_to {authn_response.in_response_to}: "
                          f'{",".join(authn_response.ava.keys())}')

        validator = Saml2ResponseValidator(
            authn_response=authn_response.xmlstr,
            recipient=recipient,
            in_response_to=in_response_to,
            accepted_time_diff=accepted_time_diff,
            authn_context_class_ref=authn_context_classref,
            return_addrs=authn_response.return_addrs,
            allowed_acrs=self.config["spid_allowed_acrs"],
        )
        try:
            validator.run()
        except Exception as e:
            logger.error(e)
            return self.handle_error(e)

        context.decorate(Context.KEY_BACKEND_METADATA_STORE, self.sp.metadata)
        if self.config.get(SAMLBackend.KEY_MEMORIZE_IDP):
            issuer = authn_response.response.issuer.text.strip()
            context.state[Context.KEY_MEMORIZED_IDP] = issuer
        context.state.pop(self.name, None)
        context.state.pop(Context.KEY_FORCE_AUTHN, None)

        logger.info(f"SAMLResponse{authn_response.xmlstr}")
        return self.auth_callback_func(
            context, self._translate_response(authn_response, context.state))
コード例 #25
0
ファイル: spidsaml2.py プロジェクト: italia/Satosa-Saml2Spid
    def authn_request(self, context, entity_id):
        """
        Do an authorization request on idp with given entity id.
        This is the start of the authorization.

        :type context: satosa.context.Context
        :type entity_id: str
        :rtype: satosa.response.Response

        :param context: The current context
        :param entity_id: Target IDP entity id
        :return: response to the user agent
        """
        self.check_blacklist(context, entity_id)

        kwargs = {}
        # fetch additional kwargs
        kwargs.update(self.get_kwargs_sign_dig_algs())

        authn_context = self.construct_requested_authn_context(entity_id)
        req_authn_context = authn_context or requested_authn_context(
            class_ref=self._authn_context)
        req_authn_context.comparison = self.config.get("spid_acr_comparison",
                                                       "minimum")

        # force_auth = true only if SpidL >= 2
        if "SpidL1" in authn_context.authn_context_class_ref[0].text:
            force_authn = "false"
        else:
            force_authn = "true"

        try:
            binding = saml2.BINDING_HTTP_POST
            destination = context.internal_data.get("target_entity_id",
                                                    entity_id)
            # SPID CUSTOMIZATION
            # client = saml2.client.Saml2Client(conf)
            client = self.sp

            logger.debug(f"binding: {binding}, destination: {destination}")

            # acs_endp, response_binding = self.sp.config.getattr("endpoints", "sp")["assertion_consumer_service"][0]
            # req_id, req = self.sp.create_authn_request(
            # destination, binding=response_binding, **kwargs)

            logger.debug(f"Redirecting user to the IdP via {binding} binding.")
            # use the html provided by pysaml2 if no template was specified or it didn't exist

            # SPID want the fqdn of the IDP as entityID, not the SSO endpoint
            # 'http://idpspid.testunical.it:8088'
            # dovrebbe essere destination ma nel caso di spid-testenv2 è entityid...
            # binding, destination = self.sp.pick_binding("single_sign_on_service", None, "idpsso", entity_id=entity_id)
            location = client.sso_location(destination, binding)
            # location = client.sso_location(entity_id, binding)

            # not used anymore thanks to avviso 11
            # location_fixed = destination  # entity_id
            # ...hope to see the SSO endpoint soon in spid-testenv2
            # returns 'http://idpspid.testunical.it:8088/sso'
            # fixed: https://github.com/italia/spid-testenv2/commit/6041b986ec87ab8515dd0d43fed3619ab4eebbe9

            # verificare qui
            # acs_endp, response_binding = self.sp.config.getattr("endpoints", "sp")["assertion_consumer_service"][0]

            authn_req = saml2.samlp.AuthnRequest()
            authn_req.force_authn = force_authn
            authn_req.destination = location
            # spid-testenv2 preleva l'attribute consumer service dalla authnRequest
            # (anche se questo sta già nei metadati...)
            authn_req.attribute_consuming_service_index = "0"

            issuer = saml2.saml.Issuer()
            issuer.name_qualifier = client.config.entityid
            issuer.text = client.config.entityid
            issuer.format = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
            authn_req.issuer = issuer

            # message id
            authn_req.id = saml2.s_utils.sid()
            authn_req.version = saml2.VERSION  # "2.0"
            authn_req.issue_instant = saml2.time_util.instant()

            name_id_policy = saml2.samlp.NameIDPolicy()
            # del(name_id_policy.allow_create)
            name_id_policy.format = NAMEID_FORMAT_TRANSIENT
            authn_req.name_id_policy = name_id_policy

            # TODO: use a parameter instead
            authn_req.requested_authn_context = req_authn_context
            authn_req.protocol_binding = binding

            assertion_consumer_service_url = client.config._sp_endpoints[
                "assertion_consumer_service"][0][0]
            authn_req.assertion_consumer_service_url = (
                assertion_consumer_service_url  # 'http://sp-fqdn/saml2/acs/'
            )

            authn_req_signed = client.sign(
                authn_req,
                sign_prepare=False,
                sign_alg=kwargs["sign_alg"],
                digest_alg=kwargs["digest_alg"],
            )
            authn_req.id

            _req_str = authn_req_signed
            logger.debug(f"AuthRequest to {destination}: {_req_str}")

            relay_state = util.rndstr()
            ht_args = client.apply_binding(
                binding,
                _req_str,
                location,
                sign=True,
                sigalg=kwargs["sign_alg"],
                relay_state=relay_state,
            )

            if self.sp.config.getattr("allow_unsolicited", "sp") is False:
                if authn_req.id in self.outstanding_queries:
                    errmsg = "Request with duplicate id {}".format(
                        authn_req.id)
                    logger.debug(errmsg)
                    raise SATOSAAuthenticationError(context.state, errmsg)
                self.outstanding_queries[authn_req.id] = authn_req_signed

            context.state[self.name] = {"relay_state": relay_state}
            # these will give the way to check compliances between the req and resp
            context.state["req_args"] = {"id": authn_req.id}

            logger.info(f"SAMLRequest: {ht_args}")
            return make_saml_response(binding, ht_args)

        except Exception as exc:
            logger.debug("Failed to construct the AuthnRequest for state")
            raise SATOSAAuthenticationError(
                context.state, "Failed to construct the AuthnRequest") from exc
コード例 #26
0
ファイル: openid_connect.py プロジェクト: borgand/SATOSA
    def callback(self, response, state, backend_state):
        """
        This is the method that should be called when an AuthN response has been
        received from the OP.
        :type response: dict[str, str]
        :type state: satosa.sate.State
        :type backend_state: dict[str, str]
        :rtype: satosa.response.Response

        :param response: The response parameters from the OP.
        :param state: A SATOSA state.
        :param backend_state: The state data for this backend module.
        :return:
        """
        authresp = self.parse_response(AuthorizationResponse,
                                       response,
                                       sformat="dict",
                                       keyjar=self.keyjar)

        if isinstance(authresp, ErrorResponse):
            if authresp["error"] == "login_required":
                satosa_logging(LOGGER, logging.WARN,
                               "Access denied for state: %s" % backend_state,
                               state)
                raise SATOSAAuthenticationError(state, "Access denied")
            else:
                satosa_logging(LOGGER, logging.DEBUG,
                               "Access denied for state: %s" % backend_state,
                               state)
                raise SATOSAAuthenticationError(state, "Access denied")
        try:
            if authresp["id_token"] != backend_state["nonce"]:
                satosa_logging(LOGGER, logging.DEBUG,
                               "Invalid nonce. for state: %s" % backend_state,
                               state)
                raise SATOSAAuthenticationError(state, "Invalid nonce")
            self.id_token[authresp["state"]] = authresp["id_token"]
        except KeyError:
            pass

        if self.behaviour["response_type"] == "code":
            # get the access token
            try:
                args = {
                    "code": authresp["code"],
                    "redirect_uri":
                    self.registration_response["redirect_uris"][0],
                    "client_id": self.client_id,
                    "client_secret": self.client_secret
                }

                atresp = self.do_access_token_request(
                    scope="openid",
                    state=authresp["state"],
                    request_args=args,
                    authn_method=self.
                    registration_response["token_endpoint_auth_method"])
            except Exception as err:
                satosa_logging(LOGGER,
                               logging.ERROR,
                               "%s" % err,
                               state,
                               exc_info=True)
                raise

            if isinstance(atresp, ErrorResponse):
                msg = "Invalid response %s." % atresp["error"]
                satosa_logging(LOGGER, logging.ERROR, msg, state)
                raise SATOSAAuthenticationError(state, msg)

        kwargs = {}
        try:
            kwargs = {"method": self.userinfo_request_method}
        except AttributeError:
            pass

        inforesp = self.do_user_info_request(state=authresp["state"], **kwargs)

        if isinstance(inforesp, ErrorResponse):
            msg = "Invalid response %s." % inforesp["error"]
            satosa_logging(LOGGER, logging.ERROR, msg, state)
            raise SATOSAAuthenticationError(
                state, "Invalid response %s." % inforesp["error"])

        userinfo = inforesp.to_dict()

        satosa_logging(LOGGER, logging.DEBUG, "UserInfo: %s" % inforesp, state)

        return userinfo
コード例 #27
0
ファイル: saml2.py プロジェクト: nsklikas/SATOSA
    def authn_request(self, context, entity_id, requested_attributes=None):
        """
        Do an authorization request on idp with given entity id.
        This is the start of the authorization.

        :type context: satosa.context.Context
        :type entity_id: str
        :type requested_attributes: list
        :rtype: satosa.response.Response

        :param context: The current context
        :param entity_id: Target IDP entity id
        :return: response to the user agent
        """

        # If IDP blacklisting is enabled and the selected IDP is blacklisted,
        # stop here
        if self.idp_blacklist_file:
            with open(self.idp_blacklist_file) as blacklist_file:
                blacklist_array = json.load(blacklist_file)['blacklist']
                if entity_id in blacklist_array:
                    msg = "IdP with EntityID {} is blacklisted".format(
                        entity_id)
                    logline = lu.LOG_FMT.format(id=lu.get_session_id(
                        context.state),
                                                message=msg)
                    logger.debug(logline, exc_info=False)
                    raise SATOSAAuthenticationError(
                        context.state,
                        "Selected IdP is blacklisted for this backend")

        try:
            binding, destination = self.sp.pick_binding(
                "single_sign_on_service", None, "idpsso", entity_id=entity_id)
            msg = "binding: {}, destination: {}".format(binding, destination)
            logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state),
                                        message=msg)
            logger.debug(logline)

            kwargs = self._get_authn_request_args(
                context, entity_id, requested_attributes=requested_attributes)
            req_id, req = self.sp.create_authn_request(destination, **kwargs)
            relay_state = util.rndstr()
            ht_args = self.sp.apply_binding(binding,
                                            "%s" % req,
                                            destination,
                                            relay_state=relay_state)
            msg = "ht_args: {}".format(ht_args)
            logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state),
                                        message=msg)
            logger.debug(logline)
        except Exception as exc:
            msg = "Failed to construct the AuthnRequest for state"
            logline = lu.LOG_FMT.format(id=lu.get_session_id(context.state),
                                        message=msg)
            logger.debug(logline, exc_info=True)
            raise SATOSAAuthenticationError(
                context.state, "Failed to construct the AuthnRequest") from exc

        if self.sp.config.getattr('allow_unsolicited', 'sp') is False:
            if req_id in self.outstanding_queries:
                msg = "Request with duplicate id {}".format(req_id)
                logline = lu.LOG_FMT.format(id=lu.get_session_id(
                    context.state),
                                            message=msg)
                logger.debug(logline)
                raise SATOSAAuthenticationError(context.state, msg)
            self.outstanding_queries[req_id] = req

        context.state[self.name] = {"relay_state": relay_state}
        return make_saml_response(binding, ht_args)
コード例 #28
0
ファイル: spidsaml2.py プロジェクト: berez23/Satosa-Saml2Spid
    def authn_response(self, context, binding):
        """
        Endpoint for the idp response
        :type context: satosa.context,Context
        :type binding: str
        :rtype: satosa.response.Response

        :param context: The current context
        :param binding: The saml binding type
        :return: response
        """
        if not context.request["SAMLResponse"]:
            logger.debug("Missing Response for state")
            raise SATOSAAuthenticationError(context.state, "Missing Response")
        
        try:
            authn_response = self.sp.parse_authn_request_response(
                context.request["SAMLResponse"],
                binding, outstanding=self.outstanding_queries)
        except Exception as err:
            logger.debug("Failed to parse authn request for state")
            raise SATOSAAuthenticationError(context.state, "Failed to parse authn request") from err

        if self.sp.config.getattr('allow_unsolicited', 'sp') is False:
            req_id = authn_response.in_response_to
            if req_id not in self.outstanding_queries:
                errmsg = "No request with id: {}".format(req_id),
                logger.debug(errmsg)
                raise SATOSAAuthenticationError(context.state, errmsg)
            del self.outstanding_queries[req_id]
        
        # Context validation
        if not context.state.get(self.name):
            _msg = "context.state[self.name] KeyError: where self.name is {}".format(self.name)
            logger.error(_msg)
            raise SATOSAStateError(context.state, _msg)
        # check if the relay_state matches the cookie state
        if context.state[self.name]["relay_state"] != context.request["RelayState"]:
            logger.debug("State did not match relay state for state")
            raise SATOSAAuthenticationError(context.state, "State did not match relay state")

        # Spid and SAML2 additional tests
        accepted_time_diff = self.config['sp_config']['accepted_time_diff']
        recipient = self.config['sp_config']['service']['sp']['endpoints']['assertion_consumer_service'][0][0]
        authn_context_classref = self.config['acr_mapping']['']

        issuer = authn_response.response.issuer
    
        # this will get the entity name in state
        if len(context.state.keys()) < 2:
            _msg = "Inconsistent context.state"
            logger.error(_msg)
            raise SATOSAStateError(context.state, _msg)
        
        destination_frontend = list(context.state.keys())[1]
        # deprecated
        # if not context.state.get('Saml2IDP'):
            # _msg = "context.state['Saml2IDP'] KeyError"
            # logger.error(_msg)
            # raise SATOSAStateError(context.state, "State without Saml2IDP")
        in_response_to = context.state['req_args']['id']
        requester = context.state['SATOSA_BASE']['requester']

        # some debug
        if authn_response.ava:
            logging.debug(f'Attributes to {authn_response.return_addrs} '
                          f'in_response_to {authn_response.in_response_to}: '
                          f'{",".join(authn_response.ava.keys())}')
        
        validator = Saml2ResponseValidator(authn_response=authn_response.xmlstr,
                                           recipient = recipient,
                                           in_response_to=in_response_to,
                                           requester = requester,
                                           accepted_time_diff = accepted_time_diff,
                                           authn_context_class_ref=authn_context_classref,
                                           return_addrs=authn_response.return_addrs)
        validator.run()
        
        context.decorate(Context.KEY_BACKEND_METADATA_STORE, self.sp.metadata)
        if self.config.get(SAMLBackend.KEY_MEMORIZE_IDP):
            issuer = authn_response.response.issuer.text.strip()
            context.state[Context.KEY_MEMORIZED_IDP] = issuer
        context.state.pop(self.name, None)
        context.state.pop(Context.KEY_FORCE_AUTHN, None)
        return self.auth_callback_func(context, self._translate_response(authn_response, context.state))
コード例 #29
0
    def authn_response(self, context, binding):
        """
        Endpoint for the idp response
        :type context: satosa.context,Context
        :type binding: str
        :rtype: satosa.response.Response

        :param context: The current context
        :param binding: The saml binding type
        :return: response
        """
        if not context.request["SAMLResponse"]:
            satosa_logging(logger, logging.DEBUG, "Missing Response for state",
                           context.state)
            raise SATOSAAuthenticationError(context.state, "Missing Response")

        try:
            authn_response = self.sp.parse_authn_request_response(
                context.request["SAMLResponse"],
                binding,
                outstanding=self.outstanding_queries)
        except Exception as err:
            satosa_logging(logger,
                           logging.DEBUG,
                           "Failed to parse authn request for state",
                           context.state,
                           exc_info=True)
            raise SATOSAAuthenticationError(
                context.state, "Failed to parse authn request") from err

        if self.sp.config.getattr('allow_unsolicited', 'sp') is False:
            req_id = authn_response.in_response_to
            if req_id not in self.outstanding_queries:
                errmsg = "No request with id: {}".format(req_id),
                satosa_logging(logger, logging.DEBUG, errmsg, context.state)
                raise SATOSAAuthenticationError(context.state, errmsg)
            del self.outstanding_queries[req_id]

        # check if the relay_state matches the cookie state
        if context.state[
                self.name]["relay_state"] != context.request["RelayState"]:
            satosa_logging(logger, logging.DEBUG,
                           "State did not match relay state for state",
                           context.state)
            raise SATOSAAuthenticationError(context.state,
                                            "State did not match relay state")

        # Spid and SAML2 additional tests
        issuer = context.state['Saml2IDP']['resp_args']['sp_entity_id']
        accepted_time_diff = self.config['sp_config']['accepted_time_diff']
        recipient = self.config['sp_config']['service']['sp']['endpoints'][
            'assertion_consumer_service'][0][0]
        authn_context_classref = self.config['acr_mapping']['']
        in_response_to = context.state['Saml2IDP']['resp_args'][
            'in_response_to']

        validator = Saml2ResponseValidator(
            authn_response=authn_response.xmlstr,
            recipient=recipient,
            in_response_to=in_response_to,
            accepted_time_diff=accepted_time_diff,
            authn_context_class_ref=authn_context_classref)
        validator.run()

        context.decorate(Context.KEY_BACKEND_METADATA_STORE, self.sp.metadata)
        if self.config.get(SAMLBackend.KEY_MEMORIZE_IDP):
            issuer = authn_response.response.issuer.text.strip()
            context.state[Context.KEY_MEMORIZED_IDP] = issuer
        context.state.pop(self.name, None)
        context.state.pop(Context.KEY_FORCE_AUTHN, None)
        return self.auth_callback_func(
            context, self._translate_response(authn_response, context.state))
コード例 #30
0
    def authn_request(self, context, entity_id):
        """
        Do an authorization request on idp with given entity id.
        This is the start of the authorization.

        :type context: satosa.context.Context
        :type entity_id: str
        :rtype: satosa.response.Response

        :param context: The current context
        :param entity_id: Target IDP entity id
        :return: response to the user agent
        """
        self.check_blacklist()

        kwargs = {}
        # fetch additional kwargs
        kwargs.update(self.get_kwargs_sign_dig_algs())

        authn_context = self.construct_requested_authn_context(entity_id)
        requested_authn_context = authn_context or requested_authn_context(
            class_ref=self._authn_context)

        # force_auth = true only if SpidL >= 2
        if 'SpidL1' in authn_context.authn_context_class_ref[0].text:
            force_authn = 'false'
        else:
            force_authn = 'true'

        try:
            binding = saml2.BINDING_HTTP_POST
            destination = context.request['entityID']
            # SPID CUSTOMIZATION
            #client = saml2.client.Saml2Client(conf)
            client = self.sp

            satosa_logging(
                logger, logging.DEBUG,
                "binding: %s, destination: %s" % (binding, destination),
                context.state)

            # acs_endp, response_binding = self.sp.config.getattr("endpoints", "sp")["assertion_consumer_service"][0]
            # req_id, req = self.sp.create_authn_request(
            # destination, binding=response_binding, **kwargs)

            logger.debug('Redirecting user to the IdP via %s binding.',
                         binding)
            # use the html provided by pysaml2 if no template was specified or it didn't exist

            # SPID want the fqdn of the IDP as entityID, not the SSO endpoint
            # 'http://idpspid.testunical.it:8088'
            # dovrebbe essere destination ma nel caso di spid-testenv2 è entityid...
            # binding, destination = self.sp.pick_binding("single_sign_on_service", None, "idpsso", entity_id=entity_id)
            # location = client.sso_location(destination, binding)
            location = client.sso_location(entity_id, binding)
            location_fixed = entity_id
            # ...hope to see the SSO endpoint soon in spid-testenv2
            # returns 'http://idpspid.testunical.it:8088/sso'
            # fixed: https://github.com/italia/spid-testenv2/commit/6041b986ec87ab8515dd0d43fed3619ab4eebbe9

            # verificare qui
            # acs_endp, response_binding = self.sp.config.getattr("endpoints", "sp")["assertion_consumer_service"][0]

            authn_req = saml2.samlp.AuthnRequest()
            authn_req.force_authn = force_authn
            authn_req.destination = location
            # spid-testenv2 preleva l'attribute consumer service dalla authnRequest (anche se questo sta già nei metadati...)
            authn_req.attribute_consuming_service_index = "0"

            # import pdb; pdb.set_trace()
            issuer = saml2.saml.Issuer()
            issuer.name_qualifier = client.config.entityid
            issuer.text = client.config.entityid
            issuer.format = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
            authn_req.issuer = issuer

            # message id
            authn_req.id = saml2.s_utils.sid()
            authn_req.version = saml2.VERSION  # "2.0"
            authn_req.issue_instant = saml2.time_util.instant()

            name_id_policy = saml2.samlp.NameIDPolicy()
            # del(name_id_policy.allow_create)
            name_id_policy.format = NAMEID_FORMAT_TRANSIENT
            authn_req.name_id_policy = name_id_policy

            # TODO: use a parameter instead
            authn_req.requested_authn_context = requested_authn_context
            authn_req.protocol_binding = binding

            assertion_consumer_service_url = client.config._sp_endpoints[
                'assertion_consumer_service'][0][0]
            authn_req.assertion_consumer_service_url = assertion_consumer_service_url  #'http://sp-fqdn/saml2/acs/'

            authn_req_signed = client.sign(authn_req,
                                           sign_prepare=False,
                                           sign_alg=kwargs['sign_alg'],
                                           digest_alg=kwargs['digest_alg'])
            session_id = authn_req.id

            _req_str = authn_req_signed
            logger.debug('AuthRequest to {}: {}'.format(
                destination, (_req_str)))

            relay_state = util.rndstr()
            ht_args = client.apply_binding(binding,
                                           _req_str,
                                           location,
                                           sign=True,
                                           sigalg=kwargs['sign_alg'],
                                           relay_state=relay_state)

            if self.sp.config.getattr('allow_unsolicited', 'sp') is False:
                if authn_req.id in self.outstanding_queries:
                    errmsg = "Request with duplicate id {}".format(req_id)
                    satosa_logging(logger, logging.DEBUG, errmsg,
                                   context.state)
                    raise SATOSAAuthenticationError(context.state, errmsg)
                self.outstanding_queries[authn_req.id] = authn_req_signed

            context.state[self.name] = {"relay_state": relay_state}

            satosa_logging(logger, logging.DEBUG, "ht_args: %s" % ht_args,
                           context.state)
            return make_saml_response(binding, ht_args)

        except Exception as exc:
            satosa_logging(logger,
                           logging.DEBUG,
                           "Failed to construct the AuthnRequest for state",
                           context.state,
                           exc_info=True)
            raise SATOSAAuthenticationError(
                context.state, "Failed to construct the AuthnRequest") from exc