예제 #1
0
파일: base.py 프로젝트: borgand/SATOSA
    def run(self, context):
        """
        Runs the satosa proxy with the given context.

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

        :param context: The request context
        :return: response
        """
        try:
            self._load_state(context)
            spec = self.module_router.endpoint_routing(context)
            resp = self._run_bound_endpoint(context, spec)
            self._save_state(resp, context)
        except SATOSAError:
            satosa_logging(LOGGER,
                           logging.ERROR,
                           "Uncaught SATOSA error",
                           context.state,
                           exc_info=True)
            raise
        except Exception as err:
            satosa_logging(LOGGER,
                           logging.ERROR,
                           "Uncaught exception",
                           context.state,
                           exc_info=True)
            raise SATOSAUnknownError("Unknown error") from err
        return resp
예제 #2
0
파일: base.py 프로젝트: borgand/SATOSA
    def _save_state(self, resp, context):
        """
        Saves a state from context to cookie

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

        :param resp: The response
        :param context: Session context
        """
        if context.state.should_delete():
            # Save empty state with a max age of 0
            cookie = state_to_cookie(State(), self.config.COOKIE_STATE_NAME,
                                     "/", self.config.STATE_ENCRYPTION_KEY, 0)
        else:
            cookie = state_to_cookie(context.state,
                                     self.config.COOKIE_STATE_NAME, "/",
                                     self.config.STATE_ENCRYPTION_KEY)

        if isinstance(resp, Response):
            resp.add_cookie(cookie)
        else:
            try:
                resp.headers.append(tuple(cookie.output().split(": ", 1)))
            except:
                satosa_logging(
                    LOGGER, logging.WARN,
                    "can't add cookie to response '%s'" % resp.__class__,
                    context.state)
                pass
예제 #3
0
파일: base.py 프로젝트: borgand/SATOSA
    def _auth_req_callback_func(self, context, internal_request):
        """
        This function is called by a frontend module when an authorization request has been
        processed.

        :type context: satosa.context.Context
        :type internal_request: satosa.internal_data.InternalRequest
        :rtype: satosa.response.Response

        :param context: The request context
        :param internal_request: request processed by the frontend

        :return: response
        """
        state = context.state
        state.add(SATOSABase.STATE_KEY, internal_request.requestor)
        satosa_logging(
            LOGGER, logging.INFO,
            "Requesting provider: {}".format(internal_request.requestor),
            state)
        context.request = None
        backend = self.module_router.backend_routing(context)
        self.consent_module.save_state(internal_request, state)
        UserIdHasher.save_state(internal_request, state)
        if self.request_micro_services:
            internal_request = self.request_micro_services.process_service_queue(
                context, internal_request)
        return backend.start_auth(context, internal_request)
예제 #4
0
파일: base.py 프로젝트: borgand/SATOSA
    def _run_bound_endpoint(self, context, spec):
        """

        :type context: satosa.context.Context
        :type spec: ((satosa.context.Context, Any) -> satosa.response.Response, Any) |
        (satosa.context.Context) -> satosa.response.Response

        :param context: The request context
        :param spec: bound endpoint function
        :return: response
        """
        try:
            if isinstance(spec, tuple):
                return spec[0](context, *spec[1:])
            else:
                return spec(context)
        except SATOSAAuthenticationError as error:
            error.error_id = uuid4().urn
            msg = "ERROR_ID [{err_id}]\nSTATE:\n{state}".format(
                err_id=error.error_id,
                state=json.dumps(error.state.state_dict, indent=4))
            satosa_logging(LOGGER,
                           logging.ERROR,
                           msg,
                           error.state,
                           exc_info=True)
            return self._handle_satosa_authentication_error(error)
예제 #5
0
파일: base.py 프로젝트: borgand/SATOSA
    def _save_state(self, resp, context):
        """
        Saves a state from context to cookie

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

        :param resp: The response
        :param context: Session context
        """
        if context.state.should_delete():
            # Save empty state with a max age of 0
            cookie = state_to_cookie(State(),
                                     self.config.COOKIE_STATE_NAME,
                                     "/",
                                     self.config.STATE_ENCRYPTION_KEY,
                                     0)
        else:
            cookie = state_to_cookie(context.state,
                                     self.config.COOKIE_STATE_NAME,
                                     "/",
                                     self.config.STATE_ENCRYPTION_KEY)

        if isinstance(resp, Response):
            resp.add_cookie(cookie)
        else:
            try:
                resp.headers.append(tuple(cookie.output().split(": ", 1)))
            except:
                satosa_logging(LOGGER, logging.WARN,
                               "can't add cookie to response '%s'" % resp.__class__, context.state)
                pass
예제 #6
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)
예제 #7
0
파일: saml2.py 프로젝트: digideskio/SATOSA
    def _handle_backend_error(self, exception, idp):
        """
        See super class satosa.frontends.base.FrontendModule

        :type exception: satosa.exception.SATOSAAuthenticationError
        :type idp: saml.server.Server
        :rtype: satosa.response.Response

        :param exception: The SATOSAAuthenticationError
        :param idp: The saml frontend idp server
        :return: A response
        """
        loaded_state = self.load_state(exception.state)
        relay_state = loaded_state["relay_state"]
        resp_args = loaded_state["resp_args"]
        error_resp = idp.create_error_response(resp_args["in_response_to"],
                                               resp_args["destination"],
                                               Exception(exception.message))
        http_args = idp.apply_binding(
            resp_args["binding"], "%s" % error_resp,
            resp_args["destination"],
            relay_state, response=True)

        satosa_logging(LOGGER, logging.DEBUG, "HTTPargs: %s" % http_args, exception.state)
        return response(resp_args["binding"], http_args)
예제 #8
0
파일: saml2.py 프로젝트: borgand/SATOSA
    def _translate_response(self, response, state):
        """
        Translates a saml authorization response to an internal response

        :type response: saml2.response.AuthnResponse
        :rtype: satosa.internal_data.InternalResponse
        :param response: The saml authorization response
        :return: A translated internal response
        """
        _authn_info = response.authn_info()[0]
        timestamp = response.assertion.authn_statement[0].authn_instant
        issuer = response.response.issuer.text
        auth_class_ref = _authn_info[0]

        auth_info = AuthenticationInformation(auth_class_ref, timestamp, issuer)
        internal_resp = InternalResponse(auth_info=auth_info)

        internal_resp.set_user_id(response.get_subject().text)
        if "user_id_params" in self.config:
            user_id = ""
            for param in self.config["user_id_params"]:
                try:
                    user_id += response.ava[param]
                except Exception as error:
                    raise SATOSAAuthenticationError from error
            internal_resp.set_user_id(user_id)

        internal_resp.add_attributes(self.converter.to_internal(self.attribute_profile, response.ava))

        satosa_logging(LOGGER, logging.DEBUG,
                       "received attributes:\n%s" % json.dumps(response.ava, indent=4), state)

        return internal_resp
예제 #9
0
    def manage_al(self, context, internal_response):
        """
        Manage account linking and recovery

        :type context: satosa.context.Context
        :type internal_response: satosa.internal_data.InternalResponse
        :rtype: satosa.response.Response

        :param context:
        :param internal_response:
        :return: response
        """

        if not self.enabled:
            return self.callback_func(context, internal_response)

        issuer = internal_response.auth_info.issuer
        id = internal_response.get_user_id()
        status_code, message = self._get_uuid(context, issuer, id)

        if status_code == 200:
            satosa_logging(LOGGER, logging.INFO, "issuer/id pair is linked in AL service",
                           context.state)
            internal_response.set_user_id(message)
            try:
                context.state.remove(AccountLinkingModule.STATE_KEY)
            except KeyError:
                pass
            return self.callback_func(context, internal_response)

        return self._approve_new_id(context, internal_response, message)
 def _populate_attributes(self, config, record, context, data):
     """
     Use a record found in LDAP to populate attributes.
     """
     search_return_attributes = config['search_return_attributes']
     for attr in search_return_attributes.keys():
         if attr in record["attributes"]:
             if record["attributes"][attr]:
                 data.attributes[search_return_attributes[attr]] = record["attributes"][attr]
                 satosa_logging(
                     logger, 
                     logging.DEBUG, 
                     "Setting internal attribute {} with values {}".format(
                         search_return_attributes[attr], 
                         record["attributes"][attr]
                         ), 
                     context.state
                     )
             else:
                 satosa_logging(
                     logger, 
                     logging.DEBUG, 
                     "Not setting internal attribute {} because value {} is null or empty".format(
                         search_return_attributes[attr], 
                         record["attributes"][attr]
                         ), 
                     context.state
                     )
예제 #11
0
파일: saml2.py 프로젝트: borgand/SATOSA
    def _handle_backend_error(self, exception, idp):
        """
        See super class satosa.frontends.base.FrontendModule

        :type exception: satosa.exception.SATOSAAuthenticationError
        :type idp: saml.server.Server
        :rtype: satosa.response.Response

        :param exception: The SATOSAAuthenticationError
        :param idp: The saml frontend idp server
        :return: A response
        """
        loaded_state = self.load_state(exception.state)
        relay_state = loaded_state["relay_state"]
        resp_args = loaded_state["resp_args"]
        error_resp = idp.create_error_response(resp_args["in_response_to"],
                                               resp_args["destination"],
                                               Exception(exception.message))
        http_args = idp.apply_binding(
            resp_args["binding"], "%s" % error_resp,
            resp_args["destination"],
            relay_state, response=True)

        satosa_logging(LOGGER, logging.DEBUG, "HTTPargs: %s" % http_args, exception.state)
        return response(resp_args["binding"], http_args)
예제 #12
0
    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),
            ))
예제 #13
0
파일: saml2.py 프로젝트: borgand/SATOSA
    def get_filter_attributes(self, idp, idp_policy, sp_entity_id, state):
        """
        Returns a list of approved attributes

        :type idp: saml.server.Server
        :type idp_policy: saml2.assertion.Policy
        :type sp_entity_id: str
        :type state: satosa.state.State
        :rtype: list[str]

        :param idp: The saml frontend idp server
        :param idp_policy: The idp policy
        :param sp_entity_id: The requesting sp entity id
        :param state: The current state
        :return: A list containing approved attributes
        """
        name_format = idp_policy.get_name_form(sp_entity_id)
        attrconvs = idp.config.attribute_converters
        idp_policy.acs = attrconvs
        attribute_filter = []
        for aconv in attrconvs:
            if aconv.name_format == name_format:
                attribute_filter = list(
                        idp_policy.restrict(aconv._to, sp_entity_id, idp.metadata).keys())
        attribute_filter = self.converter.to_internal_filter(self.attribute_profile,
                                                             attribute_filter, True)
        satosa_logging(LOGGER, logging.DEBUG, "Filter: %s" % attribute_filter, state)
        return attribute_filter
예제 #14
0
    def render_consent(self, consent_state, internal_response, language='en'):
        requester_name = consent_state.get('requester_display_name', None)
        if not requester_name:
            requester_name = self._find_requester_name(
                internal_response.requester, language)
        requester_logo = consent_state.get('requester_logo', None)
        gettext.translation('messages',
                            localedir=pkg_resources.resource_filename(
                                'svs', 'data/i18n/locale'),
                            languages=[language]).install()

        released_attributes = self._attributes_to_release(internal_response)
        template = self.template_lookup.get_template('consent.mako')
        page = template.render(
            requester_name=requester_name,
            requester_logo=self._normalize_logo(requester_logo),
            privacy_url=self.privacy_url,
            released_claims=released_attributes,
            form_action='/consent{}'.format(self.endpoint),
            language=language)
        page = Template(page).render(
            requester_name=requester_name,
            requester_logo=self._normalize_logo(requester_logo),
            privacy_url=self.privacy_url,
            released_claims=released_attributes,
            form_action='/consent{}'.format(self.endpoint),
            language=language)

        satosa_logging(logger, logging.INFO,
                       "released attributes: {}".format(released_attributes),
                       consent_state)
        return Response(page, content='text/html')
예제 #15
0
파일: util.py 프로젝트: ibrsp/SATOSA
def check_set_dict_defaults(dic, spec):
    for path, value in spec.items():
        keys = path.split('.')
        try:
            _val = dict_get_nested(dic, keys)
        except KeyError:
            if type(value) is list:
                value_default = value[0]
            else:
                value_default = value
            dict_set_nested(dic, keys, value_default)
        else:
            if type(value) is list:
                is_value_valid = _val in value
            elif type(value) is dict:
                # do not validate dict
                is_value_valid = bool(_val)
            else:
                is_value_valid = _val == value
            if not is_value_valid:
                satosa_logging(
                    logger, logging.WARNING,
                    "Incompatible configuration value '{}' for '{}'."
                    " Value shoud be: {}".format(_val, path, value),
                    {})
    return dic
예제 #16
0
    def create_authn_request(self, state, oidc_state, nonce, acr_value=None, **kwargs):
        """
        Creates an oidc authentication request.
        :type state: satosa.state.State
        :type oidc_state: str
        :type nonce: str
        :type acr_value: list[str]
        :type kwargs: Any
        :rtype: satosa.response.Redirect

        :param state: Module state
        :param oidc_state: OIDC state
        :param nonce: A nonce
        :param acr_value: Authentication type
        :param kwargs: Whatever
        :return: A redirect to the OP
        """
        request_args = self.setup_authn_request_args(acr_value, kwargs, oidc_state, nonce)

        cis = self.construct_AuthorizationRequest(request_args=request_args)
        satosa_logging(LOGGER, logging.DEBUG, "request: %s" % cis, state)

        url, body, ht_args, cis = self.uri_and_body(AuthorizationRequest, cis,
                                                    method="GET",
                                                    request_args=request_args)

        satosa_logging(LOGGER, logging.DEBUG, "body: %s" % body, state)
        satosa_logging(LOGGER, logging.INFO, "URL: %s" % url, state)
        satosa_logging(LOGGER, logging.DEBUG, "ht_args: %s" % ht_args, state)

        resp = Redirect(str(url))
        if ht_args:
            resp.headers.extend([(a, b) for a, b in ht_args.items()])
        satosa_logging(LOGGER, logging.DEBUG, "resp_headers: %s" % resp.headers, state)
        return resp
예제 #17
0
파일: base.py 프로젝트: borgand/SATOSA
    def _auth_req_callback_func(self, context, internal_request):
        """
        This function is called by a frontend module when an authorization request has been
        processed.

        :type context: satosa.context.Context
        :type internal_request: satosa.internal_data.InternalRequest
        :rtype: satosa.response.Response

        :param context: The request context
        :param internal_request: request processed by the frontend

        :return: response
        """
        state = context.state
        state.add(SATOSABase.STATE_KEY, internal_request.requestor)
        satosa_logging(LOGGER, logging.INFO,
                       "Requesting provider: {}".format(internal_request.requestor), state)
        context.request = None
        backend = self.module_router.backend_routing(context)
        self.consent_module.save_state(internal_request, state)
        UserIdHasher.save_state(internal_request, state)
        if self.request_micro_services:
            internal_request = self.request_micro_services.process_service_queue(context,
                                                                                 internal_request)
        return backend.start_auth(context, internal_request)
예제 #18
0
파일: base.py 프로젝트: digideskio/SATOSA
    def run(self, context):
        """
        Runs the satosa proxy with the given context.

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

        :param context: The request context
        :return: response
        """
        try:
            self._load_state(context)
            spec = self.module_router.endpoint_routing(context)
            resp = self._run_bound_endpoint(context, spec)
            self._save_state(resp, context)
        except SATOSANoBoundEndpointError:
            raise
        except SATOSAError:
            satosa_logging(LOGGER, logging.ERROR, "Uncaught SATOSA error", context.state,
                           exc_info=True)
            raise
        except Exception as err:
            satosa_logging(LOGGER, logging.ERROR, "Uncaught exception", context.state,
                           exc_info=True)
            raise SATOSAUnknownError("Unknown error") from err
        return resp
예제 #19
0
    def _populate_attributes(self, config, record):
        """
        Use a record found in LDAP to populate attributes.
        """

        ldap_attributes = record.get("attributes", None)
        if not ldap_attributes:
            msg = "No attributes returned with LDAP record"
            satosa_logging(logger, logging.DEBUG, msg, None)
            return

        ldap_to_internal_map = (
            config["ldap_to_internal_map"] if config["ldap_to_internal_map"]
            # Deprecated configuration. Will be removed in future.
            else config["search_return_attributes"])

        attributes = defaultdict(list)

        for attr, values in ldap_attributes.items():
            internal_attr = ldap_to_internal_map.get(attr, None)
            if not internal_attr and ";" in attr:
                internal_attr = ldap_to_internal_map.get(
                    attr.split(";")[0], None)

            if internal_attr and values:
                attributes[internal_attr].extend(values)
                msg = "Recording internal attribute {} with values {}"
                msg = msg.format(internal_attr, attributes[internal_attr])
                satosa_logging(logger, logging.DEBUG, msg, None)

        return attributes
예제 #20
0
    def manage_al(self, context, internal_response):
        """
        Manage account linking and recovery

        :type context: satosa.context.Context
        :type internal_response: satosa.internal_data.InternalResponse
        :rtype: satosa.response.Response

        :param context:
        :param internal_response:
        :return: response
        """

        if not self.enabled:
            return self.callback_func(context, internal_response)

        issuer = internal_response.auth_info.issuer
        id = internal_response.get_user_id()
        status_code, message = self._get_uuid(context, issuer, id)

        if status_code == 200:
            satosa_logging(LOGGER, logging.INFO,
                           "issuer/id pair is linked in AL service",
                           context.state)
            internal_response.set_user_id(message)
            try:
                context.state.remove(AccountLinkingModule.STATE_KEY)
            except KeyError:
                pass
            return self.callback_func(context, internal_response)

        return self._approve_new_id(context, internal_response, message)
예제 #21
0
    def _apply_filter(self, context, attributes, provider, requester):
        filter_allow = get_dict_defaults(self.attribute_allow, provider,
                                         requester)
        satosa_logging(
            logger, logging.DEBUG,
            "{} filter_allow: {}".format(self.logprefix,
                                         filter_allow), context.state)
        filter_deny = get_dict_defaults(self.attribute_deny, provider,
                                        requester)
        satosa_logging(
            logger, logging.DEBUG,
            "{} filter_deny: {}".format(self.logprefix,
                                        filter_deny), context.state)
        allow = self._filter_attributes(attributes, filter_allow)
        satosa_logging(logger, logging.DEBUG,
                       "{} allow: {}".format(self.logprefix,
                                             allow), context.state)
        deny = self._filter_attributes(attributes, filter_deny)
        satosa_logging(logger, logging.DEBUG,
                       "{} deny: {}".format(self.logprefix,
                                            deny), context.state)

        # Remove Denies (values) from Allows (values)
        for a, v in deny.items():
            for r in v:
                if a in allow and r in allow[a]:
                    allow[a].remove(r)

        result = {a: v for a, v in allow.items() if len(v)}
        satosa_logging(logger, logging.DEBUG,
                       "{} result: {}".format(self.logprefix,
                                              result), context.state)
        return result
예제 #22
0
    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),
                                       ))
예제 #23
0
파일: state.py 프로젝트: borgand/SATOSA
def state_to_cookie(state, name, path, encryption_key, max_age=STATE_COOKIE_MAX_AGE):
    """
    Saves a state to a cookie

    :type state: satosa.state.State
    :type name: str
    :type path: str
    :type encryption_key: str
    :rtype: http.cookies.SimpleCookie

    :param state: The state to save
    :param name: Name identifier of the cookie
    :param path: Endpoint path the cookie will be associated to
    :param encryption_key: Key to encrypt the state information
    :return: A cookie
    """
    satosa_logging(LOGGER, logging.DEBUG,
                   "Saving state as cookie, secure: %s, max-age: %s, path: %s" %
                   (STATE_COOKIE_SECURE, STATE_COOKIE_MAX_AGE, path), state)
    cookie = SimpleCookie()
    cookie[name] = state.urlstate(encryption_key)
    cookie[name]["secure"] = STATE_COOKIE_SECURE
    cookie[name]["path"] = path
    cookie[name]["max-age"] = max_age
    return cookie
예제 #24
0
 def _populate_attributes(self, config, record, context, data):
     """
     Use a record found in LDAP to populate attributes.
     """
     search_return_attributes = config['search_return_attributes']
     for attr in search_return_attributes.keys():
         if attr in record["attributes"]:
             if record["attributes"][attr]:
                 data.attributes[search_return_attributes[attr]] = record["attributes"][attr]
                 satosa_logging(
                     logger,
                     logging.DEBUG,
                     "Setting internal attribute {} with values {}".format(
                         search_return_attributes[attr],
                         record["attributes"][attr]
                         ),
                     context.state
                     )
             else:
                 satosa_logging(
                     logger,
                     logging.DEBUG,
                     "Not setting internal attribute {} because value {} is null or empty".format(
                         search_return_attributes[attr],
                         record["attributes"][attr]
                         ),
                     context.state
                     )
예제 #25
0
파일: oidc.py 프로젝트: borgand/SATOSA
    def handle_authn_request(self, context):
        """
        Parse and verify the authentication request and pass it on to the backend.
        :type context: satosa.context.Context
        :rtype: oic.utils.http_util.Response

        :param context: the current context
        :return: HTTP response to the client
        """

        # verify auth req (correct redirect_uri, contains nonce and response_type='id_token')
        request = urlencode(context.request)
        satosa_logging(LOGGER, logging.DEBUG, "Authn req from client: {}".format(request),
                       context.state)

        info = self.provider.auth_init(request, request_class=AuthorizationRequest)
        if isinstance(info, Response):
            satosa_logging(LOGGER, logging.ERROR, "Error in authn req: {}".format(info.message),
                           context.state)
            return info

        client_id = info["areq"]["client_id"]

        context.state.add(self.state_id, {"oidc_request": request})
        hash_type = oidc_subject_type_to_hash_type(
                self.provider.cdb[client_id].get("subject_type", self.subject_type_default))
        internal_req = InternalRequest(hash_type, client_id,
                                       self.provider.cdb[client_id].get("client_name"))

        return self.auth_req_callback_func(context, internal_req)
예제 #26
0
파일: saml2.py 프로젝트: sgomez/SATOSA
    def get_idp_entity_id(self, context):
        """
        :type context: satosa.context.Context
        :rtype: str | None

        :param context: The current context
        :return: the entity_id of the idp or None
        """

        idps = self.sp.metadata.identity_providers()
        only_one_idp_in_metadata = ("mdq"
                                    not in self.config["sp_config"]["metadata"]
                                    and len(idps) == 1)

        only_idp = only_one_idp_in_metadata and idps[0]
        target_entity_id = context.get_decoration(Context.KEY_TARGET_ENTITYID)
        force_authn = get_force_authn(context, self.config, self.sp.config)
        memorized_idp = get_memorized_idp(context, self.config, force_authn)
        entity_id = only_idp or target_entity_id or memorized_idp or None

        satosa_logging(
            logger,
            logging.INFO,
            {
                "message": "Selected IdP",
                "only_one": only_idp,
                "target_entity_id": target_entity_id,
                "force_authn": force_authn,
                "memorized_idp": memorized_idp,
                "entity_id": entity_id,
            },
            context.state,
        )
        return entity_id
예제 #27
0
파일: util.py 프로젝트: SUNET/SATOSA
def check_set_dict_defaults(dic, spec):
    for path, value in spec.items():
        keys = path.split('.')
        try:
            _val = dict_get_nested(dic, keys)
        except KeyError:
            if type(value) is list:
                value_default = value[0]
            else:
                value_default = value
            dict_set_nested(dic, keys, value_default)
        else:
            if type(value) is list:
                is_value_valid = _val in value
            elif type(value) is dict:
                # do not validate dict
                is_value_valid = bool(_val)
            else:
                is_value_valid = _val == value
            if not is_value_valid:
                satosa_logging(
                    logger, logging.WARNING,
                    "Incompatible configuration value '{}' for '{}'."
                    " Value shoud be: {}".format(_val, path, value),
                    {})
    return dic
예제 #28
0
    def _approve_new_consent(self, context, internal_response, id_hash):
        context.state[STATE_KEY]["internal_resp"] = internal_response.to_dict()

        consent_args = {
            "attr": internal_response.attributes,
            "id": id_hash,
            "redirect_endpoint":
            "%s/consent%s" % (self.base_url, self.endpoint),
            "requester_name": context.state[STATE_KEY]["requester_name"]
        }
        if self.locked_attr:
            consent_args["locked_attrs"] = [self.locked_attr]
        if 'requester_logo' in context.state[STATE_KEY]:
            consent_args["requester_logo"] = context.state[STATE_KEY][
                'requester_logo']
        try:
            ticket = self._consent_registration(consent_args)
        except (ConnectionError, UnexpectedResponseError) as e:
            satosa_logging(
                logger, logging.ERROR,
                "Consent request failed, no consent given: {}".format(str(e)),
                context.state)
            # Send an internal_response without any attributes
            internal_response.attributes = {}
            return self._end_consent(context, internal_response)

        consent_redirect = "%s/%s" % (self.redirect_url, ticket)
        return Redirect(consent_redirect)
예제 #29
0
파일: saml2.py 프로젝트: borgand/SATOSA
    def get_filter_attributes(self, idp, idp_policy, sp_entity_id, state):
        """
        Returns a list of approved attributes

        :type idp: saml.server.Server
        :type idp_policy: saml2.assertion.Policy
        :type sp_entity_id: str
        :type state: satosa.state.State
        :rtype: list[str]

        :param idp: The saml frontend idp server
        :param idp_policy: The idp policy
        :param sp_entity_id: The requesting sp entity id
        :param state: The current state
        :return: A list containing approved attributes
        """
        name_format = idp_policy.get_name_form(sp_entity_id)
        attrconvs = idp.config.attribute_converters
        idp_policy.acs = attrconvs
        attribute_filter = []
        for aconv in attrconvs:
            if aconv.name_format == name_format:
                attribute_filter = list(idp_policy.restrict(aconv._to, sp_entity_id, idp.metadata).keys())
        attribute_filter = self.converter.to_internal_filter(self.attribute_profile, attribute_filter, True)
        satosa_logging(LOGGER, logging.DEBUG, "Filter: %s" % attribute_filter, state)
        return attribute_filter
예제 #30
0
파일: oidc.py 프로젝트: borgand/SATOSA
    def handle_authn_request(self, context):
        """
        Parse and verify the authentication request and pass it on to the backend.
        :type context: satosa.context.Context
        :rtype: oic.utils.http_util.Response

        :param context: the current context
        :return: HTTP response to the client
        """

        # verify auth req (correct redirect_uri, contains nonce and response_type='id_token')
        request = urlencode(context.request)
        satosa_logging(LOGGER, logging.DEBUG,
                       "Authn req from client: {}".format(request),
                       context.state)

        info = self.provider.auth_init(request,
                                       request_class=AuthorizationRequest)
        if isinstance(info, Response):
            satosa_logging(LOGGER, logging.ERROR,
                           "Error in authn req: {}".format(info.message),
                           context.state)
            return info

        client_id = info["areq"]["client_id"]

        context.state.add(self.state_id, {"oidc_request": request})
        hash_type = oidc_subject_type_to_hash_type(
            self.provider.cdb[client_id].get("subject_type",
                                             self.subject_type_default))
        internal_req = InternalRequest(
            hash_type, client_id,
            self.provider.cdb[client_id].get("client_name"))

        return self.auth_req_callback_func(context, internal_req)
예제 #31
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)
예제 #32
0
    def accept_consent(self, context):
        """
        Endpoint for handling accepted consent.
        :type context: satosa.context.Context
        :rtype: satosa.response.Response

        :param context: response context
        :return: response
        """
        consent_state = context.state[STATE_KEY]
        saved_resp = consent_state['internal_response']
        internal_response = InternalResponse.from_dict(saved_resp)
        del context.state[STATE_KEY]

        log = {}
        log['router'] = context.state.state_dict['ROUTER']
        log['sessionid'] = context.state.state_dict['SESSION_ID']
        log['timestamp'] = saved_resp['auth_info'].get('timestamp')
        log['idp'] = saved_resp['auth_info'].get('issuer', None)
        log['rp'] = saved_resp.get('to', None)
        log['attr'] = saved_resp.get('attr', None)

        satosa_logging(logger, logging.INFO, "log: {}".format(log),
                       context.state)
        print(json.dumps(log), file=self.loghandle, end="\n")
        self.loghandle.flush()

        transaction_log(context.state,
                        self.config.get("consent_exit_order",
                                        1000), "user_consent", "accept",
                        "exit", "success", '', '', 'Consent given by the user')

        return super().process(context, internal_response)
예제 #33
0
 def process(self, context, data):
     satosa_logging(logger, logging.DEBUG,
                    "{} Processing attribute filter".format(self.logprefix),
                    context.state)
     data.attributes = self._apply_filter(context, data.attributes,
                                          data.auth_info.issuer,
                                          data.requester)
     return super().process(context, data)
예제 #34
0
파일: ping.py 프로젝트: trsau/SATOSA
    def ping_endpoint(self, context):
        """
        """
        logprefix = PingFrontend.logprefix
        satosa_logging(logger, logging.DEBUG, "{} ping returning 200 OK".format(logprefix), context.state)

        msg = " "

        return Response(msg)
예제 #35
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
예제 #36
0
파일: saml2.py 프로젝트: borgand/SATOSA
    def _metadata(self, context):
        """
        Endpoint for retrieving the backend metadata
        :type context: satosa.context.Context
        :rtype: satosa.backends.saml2.MetadataResponse

        :param context: The current context
        :return: response with metadata
        """
        satosa_logging(LOGGER, logging.DEBUG, "Sending metadata response", context.state)
        return MetadataResponse(self.sp.config)
예제 #37
0
파일: oidc.py 프로젝트: borgand/SATOSA
    def handle_authn_response(self, context, internal_resp):
        """
        See super class method satosa.frontends.base.FrontendModule#handle_authn_response
        :type context: satosa.context.Context
        :type internal_response: satosa.internal_data.InternalResponse
        :rtype oic.utils.http_util.Response
        """
        auth_req = self._get_authn_request_from_state(context.state)

        # filter attributes to return in ID Token as claims
        attributes = self.converter.from_internal(
            "openid", internal_resp.get_attributes())
        satosa_logging(
            LOGGER, logging.DEBUG,
            "Attributes delivered by backend to OIDC frontend: {}".format(
                json.dumps(attributes)), context.state)
        flattened_attributes = {k: v[0] for k, v in attributes.items()}
        requested_id_token_claims = auth_req.get("claims", {}).get("id_token")
        user_claims = self._get_user_info(flattened_attributes,
                                          requested_id_token_claims,
                                          auth_req["scope"])
        satosa_logging(
            LOGGER, logging.DEBUG,
            "Attributes filtered by requested claims/scope: {}".format(
                json.dumps(user_claims)), context.state)

        # construct epoch timestamp of reported authentication time
        auth_time = datetime.datetime.strptime(
            internal_resp.auth_info.timestamp, "%Y-%m-%dT%H:%M:%SZ")
        epoch_timestamp = (auth_time -
                           datetime.datetime(1970, 1, 1)).total_seconds()

        base_claims = {
            "client_id": auth_req["client_id"],
            "sub": internal_resp.get_user_id(),
            "nonce": auth_req["nonce"]
        }
        id_token = self.provider.id_token_as_signed_jwt(
            base_claims,
            user_info=user_claims,
            auth_time=epoch_timestamp,
            loa="",
            alg=self.sign_alg)

        oidc_client_state = auth_req.get("state")
        kwargs = {}
        if oidc_client_state:  # inlcude any optional 'state' sent by the client in the authn req
            kwargs["state"] = oidc_client_state

        auth_resp = AuthorizationResponse(id_token=id_token, **kwargs)
        http_response = auth_resp.request(
            auth_req["redirect_uri"], self._should_fragment_encode(auth_req))
        return SeeOther(http_response)
예제 #38
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)
예제 #39
0
파일: saml2.py 프로젝트: borgand/SATOSA
    def _metadata(self, context):
        """
        Endpoint for retrieving the backend metadata
        :type context: satosa.context.Context
        :rtype: satosa.backends.saml2.MetadataResponse

        :param context: The current context
        :return: response with metadata
        """
        satosa_logging(LOGGER, logging.DEBUG, "Sending metadata response",
                       context.state)
        return MetadataResponse(self.sp.config)
예제 #40
0
파일: oidc.py 프로젝트: borgand/SATOSA
 def handle_backend_error(self, exception):
     """
     See super class satosa.frontends.base.FrontendModule
     :type exception: satosa.exception.SATOSAError
     :rtype: oic.utils.http_util.Response
     """
     auth_req = self._get_authn_request_from_state(exception.state)
     error_resp = AuthorizationErrorResponse(error="access_denied",
                                             error_description=exception.message)
     satosa_logging(LOGGER, logging.DEBUG, exception.message, exception.state)
     return SeeOther(
             error_resp.request(auth_req["redirect_uri"],
                                self._should_fragment_encode(auth_req)))
예제 #41
0
파일: saml2.py 프로젝트: digideskio/SATOSA
    def construct_authn_response(self, idp, state, identity, name_id, authn, resp_args, relay_state,
                                 sign_response=True):
        """
        Constructs an auth response

        :type idp: saml.server.Server
        :type state: satosa.state.State
        :type identity: dict[str, str]
        :type name_id: saml2.saml.NameID
        :type authn: dict[str, str]
        :type resp_args: dict[str, str]
        :type relay_state: str
        :type sign_response: bool

        :param idp: The saml frontend idp server
        :param state: The current state
        :param identity: Information about an user (The ava attributes)
        :param name_id: The name id
        :param authn: auth info
        :param resp_args: response arguments
        :param relay_state: the relay state
        :param sign_response: Flag for signing the response or not
        :return: The constructed response
        """

        _resp = idp.create_authn_response(identity,
                                          name_id=name_id,
                                          authn=authn,
                                          sign_response=sign_response,
                                          **resp_args)

        http_args = idp.apply_binding(
            resp_args["binding"], "%s" % _resp, resp_args["destination"],
            relay_state, response=True)

        satosa_logging(LOGGER, logging.DEBUG, "HTTPargs: %s" % http_args, state)

        resp = None
        if http_args["data"]:
            resp = Response(http_args["data"], headers=http_args["headers"])
        else:
            for header in http_args["headers"]:
                if header[0] == "Location":
                    resp = Redirect(header[1])

        if not resp:
            msg = "Don't know how to return response"
            satosa_logging(LOGGER, logging.ERROR, msg, state)
            resp = ServiceError(msg)

        return resp
예제 #42
0
파일: saml2.py 프로젝트: borgand/SATOSA
    def construct_authn_response(self, idp, state, identity, name_id, authn, resp_args, relay_state,
                                 sign_response=True):
        """
        Constructs an auth response

        :type idp: saml.server.Server
        :type state: satosa.state.State
        :type identity: dict[str, str]
        :type name_id: saml2.saml.NameID
        :type authn: dict[str, str]
        :type resp_args: dict[str, str]
        :type relay_state: str
        :type sign_response: bool

        :param idp: The saml frontend idp server
        :param state: The current state
        :param identity: Information about an user (The ava attributes)
        :param name_id: The name id
        :param authn: auth info
        :param resp_args: response arguments
        :param relay_state: the relay state
        :param sign_response: Flag for signing the response or not
        :return: The constructed response
        """

        _resp = idp.create_authn_response(identity,
                                          name_id=name_id,
                                          authn=authn,
                                          sign_response=sign_response,
                                          **resp_args)

        http_args = idp.apply_binding(
            resp_args["binding"], "%s" % _resp, resp_args["destination"],
            relay_state, response=True)

        satosa_logging(LOGGER, logging.DEBUG, "HTTPargs: %s" % http_args, state)

        resp = None
        if http_args["data"]:
            resp = Response(http_args["data"], headers=http_args["headers"])
        else:
            for header in http_args["headers"]:
                if header[0] == "Location":
                    resp = Redirect(header[1])

        if not resp:
            msg = "Don't know how to return response"
            satosa_logging(LOGGER, logging.ERROR, msg, state)
            resp = ServiceError(msg)

        return resp
예제 #43
0
    def _metadata_endpoint(self, context):
        """
        Endpoint for retrieving the backend metadata
        :type context: satosa.context.Context
        :rtype: satosa.response.Response

        :param context: The current context
        :return: response with metadata
        """
        satosa_logging(logger, logging.DEBUG, "Sending metadata response",
                       context.state)
        conf = self.sp.config
        metadata = entity_descriptor(conf)
        # creare gli attribute_consuming_service
        cnt = 0
        for attribute_consuming_service in metadata.spsso_descriptor.attribute_consuming_service:
            attribute_consuming_service.index = str(cnt)
            cnt += 1

        cnt = 0
        for assertion_consumer_service in metadata.spsso_descriptor.assertion_consumer_service:
            assertion_consumer_service.is_default = 'true' if not cnt else ''
            assertion_consumer_service.index = str(cnt)
            cnt += 1

        # nameformat patch... tutto questo non rispecchia gli standard OASIS
        for reqattr in metadata.spsso_descriptor.attribute_consuming_service[
                0].requested_attribute:
            reqattr.name_format = None
            reqattr.friendly_name = None

        # attribute consuming service service name patch
        service_name = metadata.spsso_descriptor.attribute_consuming_service[
            0].service_name[0]
        service_name.lang = 'it'
        service_name.text = metadata.entity_id

        # remove extension disco and uuinfo (spid-testenv2)
        #metadata.spsso_descriptor.extensions = []

        # metadata signature
        secc = security_context(conf)
        #
        sign_dig_algs = self.get_kwargs_sign_dig_algs()
        eid, xmldoc = sign_entity_descriptor(metadata, None, secc,
                                             **sign_dig_algs)

        valid_instance(eid)
        return Response(text_type(xmldoc).encode('utf-8'),
                        content="text/xml; charset=utf8")
예제 #44
0
파일: oidc.py 프로젝트: borgand/SATOSA
 def handle_backend_error(self, exception):
     """
     See super class satosa.frontends.base.FrontendModule
     :type exception: satosa.exception.SATOSAError
     :rtype: oic.utils.http_util.Response
     """
     auth_req = self._get_authn_request_from_state(exception.state)
     error_resp = AuthorizationErrorResponse(
         error="access_denied", error_description=exception.message)
     satosa_logging(LOGGER, logging.DEBUG, exception.message,
                    exception.state)
     return SeeOther(
         error_resp.request(auth_req["redirect_uri"],
                            self._should_fragment_encode(auth_req)))
예제 #45
0
    def _register_client(self, context):
        http_authz = context.wsgi_environ.get("HTTP_AUTHORIZATION")
        try:
            post_body = get_post(context.wsgi_environ)
            http_resp = self.OP.register_client(http_authz, post_body)
        except OIDCFederationError as e:
            satosa_logging(LOGGER, logging.ERROR,
                           "OIDCFederation frontend error: {}".format(str(e)), context.state)
            return Response(str(e))

        if not isinstance(http_resp, Created):
            return http_resp

        return self._fixup_registration_response(http_resp)
예제 #46
0
    def _metadata_endpoint(self, context):
        """
        Endpoint for retrieving the backend metadata
        :type context: satosa.context.Context
        :rtype: satosa.response.Response

        :param context: The current context
        :return: response with metadata
        """
        satosa_logging(logger, logging.DEBUG, "Sending metadata response", context.state)

        metadata_string = create_metadata_string(None, self.sp.config, 4, None, None, None, None,
                                                 None).decode("utf-8")
        return Response(metadata_string, content="text/xml")
예제 #47
0
    def process(self, context: Context, data: InternalResponse) -> InternalResponse:
        """
        :param context: The current context
        :param data: Internal response from backend
        """
        # Send stat to service
        try:
            ticket = self._get_ticket()
            self._register(data.to_requestor, data.auth_info.issuer, ticket)
        except requests.ConnectionError as e:
            satosa_logging(LOGGER, logging.ERROR, "Could not connect to the statistics service '{}'".format(self.stat_uri), context.state)
        except Exception as e:
            satosa_logging(LOGGER, logging.ERROR, "Could not connect to the statistics service '{}'".format(self.stat_uri), context.state, exc_info=True)

        return data
예제 #48
0
    def backend_routing(self, context):
        """
        Returns the targeted backend and an updated state

        :type context: satosa.context.Context
        :rtype satosa.backends.base.BackendModule

        :param context: The request context
        :return: backend
        """
        state = context.state
        satosa_logging(LOGGER, logging.INFO, "Routing to backend: %s " % context.target_backend,
                       state)
        backend = self.backends[context.target_backend]["instance"]
        state.add(ModuleRouter.STATE_KEY, context.target_frontend)
        return backend
예제 #49
0
    def endpoint_routing(self, context):
        """
        Finds and returns the endpoint function bound to the path

        :type context: satosa.context.Context
        :rtype: ((satosa.context.Context, Any) -> Any, Any)

        :param context: The request context
        :return: registered endpoint and bound parameters
        """
        satosa_logging(LOGGER, logging.DEBUG, "Routing path: %s" % context.path, context.state)
        self._validate_context(context)

        path_split = context.path.split('/')
        backend = path_split[0]

        if backend in self.backends:
            context.target_backend = backend

        # Search for frontend endpoint
        for frontend in self.frontends.keys():
            for regex, spec in self.frontends[frontend]["endpoints"]:
                match = re.search(regex, context.path)
                if match is not None:
                    context.target_frontend = frontend
                    msg = "Frontend request. Module name:'{name}', endpoint: {endpoint}".format(
                        name=frontend,
                        endpoint=context.path)
                    satosa_logging(LOGGER, logging.INFO, msg, context.state)
                    return spec

        if backend in self.backends:
            # Search for backend endpoint
            for regex, spec in self.backends[backend]["endpoints"]:
                match = re.search(regex, context.path)
                if match is not None:
                    msg = "Backend request. Module name:'{name}', endpoint: {endpoint}".format(
                        name=backend,
                        endpoint=context.path)
                    satosa_logging(LOGGER, logging.INFO, msg, context.state)
                    return spec
            satosa_logging(LOGGER, logging.DEBUG, "%s not bound to any function" % context.path,
                           context.state)
        else:
            satosa_logging(LOGGER, logging.DEBUG, "Unknown backend %s" % backend, context.state)

        raise SATOSANoBoundEndpointError("'{}' not bound to any function".format(context.path))
예제 #50
0
    def frontend_routing(self, context):
        """
        Returns the targeted frontend and original state

        :type context: satosa.context.Context
        :rtype satosa.frontends.base.FrontendModule

        :param context: The response context
        :return: frontend
        """

        state = context.state
        target_frontend = state.get(ModuleRouter.STATE_KEY)
        satosa_logging(LOGGER, logging.INFO, "Routing to frontend: %s " % target_frontend, state)
        context.target_frontend = target_frontend
        frontend = self.frontends[context.target_frontend]["instance"]
        return frontend
예제 #51
0
    def _validate_context(self, context):
        """
        Validates values in the context needed by the ModuleRouter.
        Raises BadContextError if any error.

        :type context: satosa.context.Context
        :rtype: None

        :param context: The request context
        :return: None
        """
        if not context:
            satosa_logging(LOGGER, logging.DEBUG, "Context was None!", context.state)
            raise SATOSABadContextError("Context is None")
        if context.path is None:
            satosa_logging(LOGGER, logging.DEBUG, "Context did not contain a path!", context.state)
            raise SATOSABadContextError("Context did not contain any path")
예제 #52
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
예제 #53
0
파일: oidc.py 프로젝트: borgand/SATOSA
    def handle_authn_response(self, context, internal_resp):
        """
        See super class method satosa.frontends.base.FrontendModule#handle_authn_response
        :type context: satosa.context.Context
        :type internal_response: satosa.internal_data.InternalResponse
        :rtype oic.utils.http_util.Response
        """
        auth_req = self._get_authn_request_from_state(context.state)

        # filter attributes to return in ID Token as claims
        attributes = self.converter.from_internal("openid", internal_resp.get_attributes())
        satosa_logging(LOGGER, logging.DEBUG,
                       "Attributes delivered by backend to OIDC frontend: {}".format(
                               json.dumps(attributes)), context.state)
        flattened_attributes = {k: v[0] for k, v in attributes.items()}
        requested_id_token_claims = auth_req.get("claims", {}).get("id_token")
        user_claims = self._get_user_info(flattened_attributes,
                                          requested_id_token_claims,
                                          auth_req["scope"])
        satosa_logging(LOGGER, logging.DEBUG,
                       "Attributes filtered by requested claims/scope: {}".format(
                               json.dumps(user_claims)), context.state)

        # construct epoch timestamp of reported authentication time
        auth_time = datetime.datetime.strptime(internal_resp.auth_info.timestamp,
                                               "%Y-%m-%dT%H:%M:%SZ")
        epoch_timestamp = (auth_time - datetime.datetime(1970, 1, 1)).total_seconds()

        base_claims = {"client_id": auth_req["client_id"],
                       "sub": internal_resp.get_user_id(),
                       "nonce": auth_req["nonce"]}
        id_token = self.provider.id_token_as_signed_jwt(base_claims, user_info=user_claims,
                                                        auth_time=epoch_timestamp,
                                                        loa="",
                                                        alg=self.sign_alg)

        oidc_client_state = auth_req.get("state")
        kwargs = {}
        if oidc_client_state:  # inlcude any optional 'state' sent by the client in the authn req
            kwargs["state"] = oidc_client_state

        auth_resp = AuthorizationResponse(id_token=id_token, **kwargs)
        http_response = auth_resp.request(auth_req["redirect_uri"],
                                          self._should_fragment_encode(auth_req))
        return SeeOther(http_response)
예제 #54
0
    def _approve_new_id(self, context, internal_response, ticket):
        """
        Redirect the user to approve the new id

        :type context: satosa.context.Context
        :type internal_response: satosa.internal_data.InternalResponse
        :type ticket: str
        :rtype: satosa.response.Redirect

        :param context: The current context
        :param internal_response: The internal response
        :param ticket: The ticket given by the al service
        :return: A redirect to approve the new id linking
        """
        satosa_logging(LOGGER, logging.INFO, "A new ID must be linked by the AL service",
                       context.state)
        context.state.add(AccountLinkingModule.STATE_KEY, internal_response.to_dict())
        return Redirect("%s/%s" % (self.al_redirect, ticket))
예제 #55
0
파일: saml2.py 프로젝트: SUNET/SATOSA
    def _translate_response(self, response, state):
        """
        Translates a saml authorization response to an internal response

        :type response: saml2.response.AuthnResponse
        :rtype: satosa.internal.InternalData
        :param response: The saml authorization response
        :return: A translated internal response
        """

        # The response may have been encrypted by the IdP so if we have an
        # encryption key, try it.
        if self.encryption_keys:
            response.parse_assertion(self.encryption_keys)

        authn_info = response.authn_info()[0]
        auth_class_ref = authn_info[0]
        timestamp = response.assertion.authn_statement[0].authn_instant
        issuer = response.response.issuer.text

        auth_info = AuthenticationInformation(
            auth_class_ref, timestamp, issuer,
        )

        # The SAML response may not include a NameID.
        subject = response.get_subject()
        name_id = subject.text if subject else None
        name_id_format = subject.format if subject else None

        attributes = self.converter.to_internal(
            self.attribute_profile, response.ava,
        )

        internal_resp = InternalData(
            auth_info=auth_info,
            attributes=attributes,
            subject_type=name_id_format,
            subject_id=name_id,
        )

        satosa_logging(logger, logging.DEBUG,
                       "backend received attributes:\n%s" %
                       json.dumps(response.ava, indent=4), state)
        return internal_resp
예제 #56
0
파일: saml2.py 프로젝트: SUNET/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["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)