Example #1
0
    def test_handle_authn_response_returns_id_token_for_verified_affiliation(
            self, signing_key_path, context, scope_value, affiliation):
        authn_req = AuthorizationRequest(
            scope='openid ' + scope_value,
            client_id='client1',
            redirect_uri='https://client.example.com',
            response_type='id_token')
        context.state[self.frontend.name] = {
            'oidc_request': authn_req.to_urlencoded()
        }
        internal_response = InternalResponse(
            AuthenticationInformation(None, str(datetime.now()),
                                      'https://idp.example.com'))
        internal_response.attributes['affiliation'] = [affiliation]
        internal_response.user_id = 'user1'

        resp = self.frontend.handle_authn_response(context, internal_response)
        auth_resp = AuthorizationResponse().from_urlencoded(
            urlparse(resp.message).fragment)

        id_token = IdToken().from_jwt(
            auth_resp['id_token'],
            key=[RSAKey(key=rsa_load(signing_key_path))])
        assert id_token['iss'] == self.frontend.base_url
        assert id_token['aud'] == ['client1']
        assert id_token['auth_time'] == internal_response.auth_info.timestamp
Example #2
0
 def handle_response(self, context):
     auth_info = AuthenticationInformation("test", str(datetime.now()),
                                           "test_issuer")
     internal_resp = InternalResponse(auth_info=auth_info)
     internal_resp.attributes = context.request
     internal_resp.user_id = "test_user"
     return self.auth_callback_func(context, internal_resp)
Example #3
0
 def setup(self, signing_key_path):
     self.account_linking_config = {
         "enable": True,
         "rest_uri": "https://localhost:8167",
         "redirect": "https://localhost:8167/approve",
         "endpoint": "handle_account_linking",
         "sign_key": signing_key_path,
         "verify_ssl": False
     }
     self.satosa_config = {
         "BASE": "https://proxy.example.com",
         "USER_ID_HASH_SALT": "qwerty",
         "COOKIE_STATE_NAME": "SATOSA_SATE",
         "STATE_ENCRYPTION_KEY": "ASDasd123",
         "PLUGIN_PATH": "",
         "BACKEND_MODULES": "",
         "FRONTEND_MODULES": "",
         "INTERNAL_ATTRIBUTES": {},
         "ACCOUNT_LINKING": self.account_linking_config
     }
     self.callback_func = MagicMock()
     self.context = Context()
     state = State()
     self.context.state = state
     auth_info = AuthenticationInformation("auth_class_ref", "timestamp",
                                           "issuer")
     self.internal_response = InternalResponse(auth_info=auth_info)
Example #4
0
 def internal_response(self):
     auth_info = AuthenticationInformation("auth_class_ref", "timestamp",
                                           "issuer")
     internal_response = InternalResponse(auth_info=auth_info)
     internal_response.requester = "client"
     internal_response.attributes = ATTRIBUTES
     return internal_response
Example #5
0
 def internal_response(self, idp_conf):
     auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z",
                                           idp_conf["entityid"])
     internal_response = InternalResponse(auth_info=auth_info)
     internal_response.attributes = AttributeMapper(
         INTERNAL_ATTRIBUTES).to_internal("saml", USERS["testuser1"])
     return internal_response
Example #6
0
    def test_auth_resp_callback_func_user_id_from_attrs_is_used_to_override_user_id(
            self, context, satosa_config):
        satosa_config["INTERNAL_ATTRIBUTES"]["user_id_from_attrs"] = [
            "user_id", "domain"
        ]
        base = SATOSABase(satosa_config)

        internal_resp = InternalResponse(AuthenticationInformation("", "", ""))
        internal_resp.attributes = {
            "user_id": ["user"],
            "domain": ["@example.com"]
        }
        internal_resp.requester = "test_requester"
        context.state[satosa.base.STATE_KEY] = {"requester": "test_requester"}
        context.state[satosa.routing.
                      STATE_KEY] = satosa_config["FRONTEND_MODULES"][0]["name"]
        UserIdHasher.save_state(InternalRequest(UserIdHashType.persistent, ""),
                                context.state)

        base._auth_resp_callback_func(context, internal_resp)

        expected_user_id = UserIdHasher.hash_data(
            satosa_config["USER_ID_HASH_SALT"], "*****@*****.**")
        expected_user_id = UserIdHasher.hash_id(
            satosa_config["USER_ID_HASH_SALT"], expected_user_id,
            internal_resp.requester, context.state)
        assert internal_resp.user_id == expected_user_id
Example #7
0
    def setup_for_authn_response(self, context, frontend, auth_req):
        context.state[frontend.name] = {"oidc_request": auth_req.to_urlencoded()}

        auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", "unittest_idp.xml")
        internal_response = InternalResponse(auth_info=auth_info)
        internal_response.attributes = AttributeMapper(INTERNAL_ATTRIBUTES).to_internal("saml", USERS["testuser1"])
        internal_response.user_id = USERS["testuser1"]["eduPersonTargetedID"][0]

        return internal_response
Example #8
0
    def test_filter_attribute_not_in_response(self):
        attribute_filters = {"": {"": {"a0": "foo:bar"}}}
        filter_service = self.create_filter_service(attribute_filters)

        resp = InternalResponse(AuthenticationInformation(None, None, None))
        resp.attributes = {
            "a1": ["abc:xyz", "1:foo:bar:2"],
        }
        filtered = filter_service.process(None, resp)
        assert filtered.attributes == {"a1": ["abc:xyz", "1:foo:bar:2"]}
Example #9
0
    def test_filter_one_attribute_from_all_target_providers_for_all_requesters(
            self):
        attribute_filters = {"": {"": {"a2": "^foo:bar$"}}}
        filter_service = self.create_filter_service(attribute_filters)

        resp = InternalResponse(AuthenticationInformation(None, None, None))
        resp.attributes = {
            "a1": ["abc:xyz"],
            "a2": ["foo:bar", "1:foo:bar:2"],
        }
        filtered = filter_service.process(None, resp)
        assert filtered.attributes == {"a1": ["abc:xyz"], "a2": ["foo:bar"]}
Example #10
0
    def test_auth_resp_callback_func_respects_user_id_to_attr(self, context, satosa_config):
        satosa_config["INTERNAL_ATTRIBUTES"]["user_id_to_attr"] = "user_id"
        base = SATOSABase(satosa_config)

        internal_resp = InternalResponse(AuthenticationInformation("", "", ""))
        internal_resp.user_id = "user1234"
        context.state[satosa.base.STATE_KEY] = {"requester": "test_requester"}
        context.state[satosa.routing.STATE_KEY] = satosa_config["FRONTEND_MODULES"][0]["name"]
        UserIdHasher.save_state(InternalRequest(UserIdHashType.transient, ""), context.state)

        base._auth_resp_callback_func(context, internal_resp)
        assert internal_resp.attributes["user_id"] == [internal_resp.user_id]
Example #11
0
    def test_filter_one_attribute_for_one_target_provider(self):
        target_provider = "test_provider"
        attribute_filters = {target_provider: {"": {"a1": "foo:bar"}}}
        filter_service = self.create_filter_service(attribute_filters)

        resp = InternalResponse(
            AuthenticationInformation(None, None, target_provider))
        resp.attributes = {
            "a1": ["abc:xyz", "1:foo:bar:2"],
        }
        filtered = filter_service.process(None, resp)
        assert filtered.attributes == {"a1": ["1:foo:bar:2"]}
Example #12
0
    def test_handle_authn_response_returns_error_access_denied_for_wrong_affiliation(self, context, scope_value,
                                                                                     affiliation):
        authn_req = AuthorizationRequest(scope='openid ' + scope_value, client_id='client1',
                                         redirect_uri='https://client.example.com',
                                         response_type='id_token')
        context.state[self.frontend.name] = {'oidc_request': authn_req.to_urlencoded()}
        internal_response = InternalResponse()
        internal_response.attributes['affiliation'] = [affiliation]
        internal_response.user_id = 'user1'

        resp = self.frontend.handle_authn_response(context, internal_response)
        auth_resp = AuthorizationErrorResponse().from_urlencoded(urlparse(resp.message).fragment)
        assert auth_resp['error'] == 'access_denied'
Example #13
0
    def _translate_response(self, response, issuer, subject_type):
        """
        Translates oidc response to SATOSA internal response.
        :type response: dict[str, str]
        :type issuer: str
        :type subject_type: str
        :rtype: InternalResponse

        :param response: Dictioary with attribute name as key.
        :param issuer: The oidc op that gave the repsonse.
        :param subject_type: public or pairwise according to oidc standard.
        :return: A SATOSA internal response.
        """
        oidc_clients = self.get_oidc_clients()
        subject_type = subject_type
        auth_info = AuthenticationInformation(UNSPECIFIED, str(datetime.now()),
                                              issuer)

        internal_resp = InternalResponse(auth_info=auth_info)

        internal_resp.add_attributes(
            self.converter.to_internal("openid", response))
        internal_resp.set_user_id(response["sub"])
        if self.config.USER_ID_PARAMAS:
            user_id = ""
            for param in self.config.USER_ID_PARAMAS:
                try:
                    user_id += response[param]
                except Exception as error:
                    raise SATOSAAuthenticationError from error
            internal_resp.set_user_id(user_id)

        return internal_resp
Example #14
0
    def setup_for_authn_response(self, context, frontend, auth_req):
        context.state[frontend.name] = {
            "oidc_request": auth_req.to_urlencoded()
        }

        auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z",
                                              "unittest_idp.xml")
        internal_response = InternalResponse(auth_info=auth_info)
        internal_response.attributes = AttributeMapper(
            INTERNAL_ATTRIBUTES).to_internal("saml", USERS["testuser1"])
        internal_response.user_id = USERS["testuser1"]["eduPersonTargetedID"][
            0]

        return internal_response
Example #15
0
    def test_auth_resp_callback_func_respects_user_id_to_attr(
            self, context, satosa_config):
        satosa_config["INTERNAL_ATTRIBUTES"]["user_id_to_attr"] = "user_id"
        base = SATOSABase(satosa_config)

        internal_resp = InternalResponse(AuthenticationInformation("", "", ""))
        internal_resp.user_id = "user1234"
        context.state[satosa.base.STATE_KEY] = {"requester": "test_requester"}
        context.state[satosa.routing.
                      STATE_KEY] = satosa_config["FRONTEND_MODULES"][0]["name"]
        UserIdHasher.save_state(InternalRequest(UserIdHashType.transient, ""),
                                context.state)

        base._auth_resp_callback_func(context, internal_resp)
        assert internal_resp.attributes["user_id"] == [internal_resp.user_id]
 def test_authz_deny_fail(self):
     attribute_deny = {"": {"default": {"a0": ['foo1', 'foo2']}}}
     attribute_allow = {}
     authz_service = self.create_authz_service(attribute_allow,
                                               attribute_deny)
     resp = InternalResponse(AuthenticationInformation(None, None, None))
     resp.attributes = {
         "a0": ["foo3"],
     }
     try:
         ctx = Context()
         ctx.state = dict()
         authz_service.process(ctx, resp)
     except SATOSAAuthenticationError as ex:
         assert False
Example #17
0
    def setup_for_authn_response(self, auth_req):
        context = Context()
        context.state = self.create_state(auth_req)

        auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", "unittest_idp.xml")
        internal_response = InternalResponse(auth_info=auth_info)
        internal_response.add_attributes(
                DataConverter(INTERNAL_ATTRIBUTES).to_internal("saml", USERS["testuser1"]))
        internal_response.set_user_id(USERS["testuser1"]["eduPersonTargetedID"][0])

        self.instance.provider.cdb = {
            "client1": {"response_types": ["id_token"],
                        "redirect_uris": [(auth_req["redirect_uri"], None)],
                        "client_salt": "salt"}}

        return context, internal_response
Example #18
0
    def change_language(self, context):
        consent_state = context.state[consent.STATE_KEY]
        saved_resp = consent_state['internal_response']
        internal_response = InternalResponse.from_dict(saved_resp)

        lang = context.request.get('lang', 'en')
        return self.render_consent(consent_state, internal_response, lang)
Example #19
0
 def setup(self, signing_key_path):
     self.account_linking_config = {
         "enable": True,
         "rest_uri": "https://localhost:8167",
         "redirect": "https://localhost:8167/approve",
         "endpoint": "handle_account_linking",
         "sign_key": signing_key_path,
         "verify_ssl": False
     }
     self.satosa_config = {
         "BASE": "https://proxy.example.com",
         "USER_ID_HASH_SALT": "qwerty",
         "COOKIE_STATE_NAME": "SATOSA_SATE",
         "STATE_ENCRYPTION_KEY": "ASDasd123",
         "PLUGIN_PATH": "",
         "BACKEND_MODULES": "",
         "FRONTEND_MODULES": "",
         "INTERNAL_ATTRIBUTES": {},
         "ACCOUNT_LINKING": self.account_linking_config
     }
     self.callback_func = MagicMock()
     self.context = Context()
     state = State()
     self.context.state = state
     auth_info = AuthenticationInformation("auth_class_ref", "timestamp", "issuer")
     self.internal_response = InternalResponse(auth_info=auth_info)
Example #20
0
    def change_language(self, context):
        consent_state = context.state[self.name]
        saved_resp = consent_state['internal_response']
        internal_response = InternalResponse.from_dict(saved_resp)

        lang = context.request.get('lang', 'en')
        return self.render_consent(internal_response, lang)
Example #21
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)
Example #22
0
    def handle_consent(self, context):
        consent_state = context.state[STATE_KEY]
        saved_resp = consent_state['internal_response']
        internal_response = InternalResponse.from_dict(saved_resp)

        lang = context.request.get('lang', 'en')
        return self.render_consent(consent_state, internal_response, lang)
Example #23
0
    def test_auth_resp_callback_func_hashes_all_specified_attributes(self, context, satosa_config):
        satosa_config["INTERNAL_ATTRIBUTES"]["hash"] = ["user_id", "mail"]
        base = SATOSABase(satosa_config)

        attributes = {"user_id": ["user"], "mail": ["*****@*****.**", "*****@*****.**"]}
        internal_resp = InternalResponse(AuthenticationInformation("", "", ""))
        internal_resp.attributes = copy.copy(attributes)
        internal_resp.user_id = "test_user"
        UserIdHasher.save_state(InternalRequest(UserIdHashType.transient, ""), context.state)
        context.state[satosa.base.STATE_KEY] = {"requester": "test_requester"}
        context.state[satosa.routing.STATE_KEY] = satosa_config["FRONTEND_MODULES"][0]["name"]

        base._auth_resp_callback_func(context, internal_resp)
        for attr in satosa_config["INTERNAL_ATTRIBUTES"]["hash"]:
            assert internal_resp.attributes[attr] == [UserIdHasher.hash_data(satosa_config["USER_ID_HASH_SALT"], v)
                                                      for v in attributes[attr]]
Example #24
0
    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
Example #25
0
def test_set_user_id():
    uid = "my_id"
    attributes = {"attr_1": "v1", "attr_2": "v2", "attr_3": "v3"}
    internal_response = InternalResponse(UserIdHashType.persistent)
    internal_response.add_attributes(attributes)
    internal_response.set_user_id(uid)
    assert uid == internal_response.get_user_id()
Example #26
0
    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
Example #27
0
    def _translate_response(self, response, issuer, subject_type):
        """
        Translates oidc response to SATOSA internal response.
        :type response: dict[str, str]
        :type issuer: str
        :type subject_type: str
        :rtype: InternalResponse

        :param response: Dictioary with attribute name as key.
        :param issuer: The oidc op that gave the repsonse.
        :param subject_type: public or pairwise according to oidc standard.
        :return: A SATOSA internal response.
        """
        oidc_clients = self.get_oidc_clients()
        subject_type = subject_type
        auth_info = AuthenticationInformation(UNSPECIFIED, str(datetime.now()), issuer)

        internal_resp = InternalResponse(
            auth_info=auth_info
        )

        internal_resp.add_attributes(self.converter.to_internal("openid", response))
        internal_resp.set_user_id(response["sub"])
        if self.config.USER_ID_PARAMAS:
            user_id = ""
            for param in self.config.USER_ID_PARAMAS:
                try:
                    user_id += response[param]
                except Exception as error:
                    raise SATOSAAuthenticationError from error
            internal_resp.set_user_id(user_id)

        return internal_resp
Example #28
0
    def test_handle_authn_response_returns_error_access_denied_for_wrong_affiliation(
            self, context, scope_value, affiliation):
        authn_req = AuthorizationRequest(
            scope='openid ' + scope_value,
            client_id='client1',
            redirect_uri='https://client.example.com',
            response_type='id_token')
        context.state[self.frontend.name] = {
            'oidc_request': authn_req.to_urlencoded()
        }
        internal_response = InternalResponse()
        internal_response.attributes['affiliation'] = [affiliation]
        internal_response.user_id = 'user1'

        resp = self.frontend.handle_authn_response(context, internal_response)
        auth_resp = AuthorizationErrorResponse().from_urlencoded(
            urlparse(resp.message).fragment)
        assert auth_resp['error'] == 'access_denied'
Example #29
0
    def test_handle_authn_response_returns_id_token_for_verified_affiliation(
            self, signing_key_path, context, scope_value, affiliation):
        authn_req = AuthorizationRequest(scope='openid ' + scope_value, client_id='client1',
                                         redirect_uri='https://client.example.com',
                                         response_type='id_token')
        context.state[self.frontend.name] = {'oidc_request': authn_req.to_urlencoded()}
        internal_response = InternalResponse(AuthenticationInformation(None, str(datetime.now()),
                                                                       'https://idp.example.com'))
        internal_response.attributes['affiliation'] = [affiliation]
        internal_response.user_id = 'user1'

        resp = self.frontend.handle_authn_response(context, internal_response)
        auth_resp = AuthorizationResponse().from_urlencoded(urlparse(resp.message).fragment)

        id_token = IdToken().from_jwt(auth_resp['id_token'], key=[RSAKey(key=rsa_load(signing_key_path))])
        assert id_token['iss'] == self.frontend.base_url
        assert id_token['aud'] == ['client1']
        assert id_token['auth_time'] == internal_response.auth_info.timestamp
Example #30
0
    def test_auth_resp_callback_func_user_id_from_attrs_is_used_to_override_user_id(self, context, satosa_config):
        satosa_config["INTERNAL_ATTRIBUTES"]["user_id_from_attrs"] = ["user_id", "domain"]
        base = SATOSABase(satosa_config)

        internal_resp = InternalResponse(AuthenticationInformation("", "", ""))
        internal_resp.attributes = {"user_id": ["user"], "domain": ["@example.com"]}
        internal_resp.requester = "test_requester"
        context.state[satosa.base.STATE_KEY] = {"requester": "test_requester"}
        context.state[satosa.routing.STATE_KEY] = satosa_config["FRONTEND_MODULES"][0]["name"]
        UserIdHasher.save_state(InternalRequest(UserIdHashType.persistent, ""), context.state)

        base._auth_resp_callback_func(context, internal_resp)

        expected_user_id = UserIdHasher.hash_data(satosa_config["USER_ID_HASH_SALT"], "*****@*****.**")
        expected_user_id = UserIdHasher.hash_id(satosa_config["USER_ID_HASH_SALT"],
                                                expected_user_id,
                                                internal_resp.requester,
                                                context.state)
        assert internal_resp.user_id == expected_user_id
Example #31
0
 def _handle_endpoint(self, context):
     """
     The resume endpoint handler. It's main duty is restoring internal_response
     and checking the resume condition
     """
     logger.info("Handle BreakOut endpoint")
     breakout_state = context.state[STATE_KEY]
     saved_response = breakout_state["internal_resp"]
     logger.info("internal_resp: %s" % saved_response)
     internal_response = InternalResponse.from_dict(saved_response)
     self.resumed = True
     return self._check_requirement(context, internal_response)
Example #32
0
    def test_handle_authn_request_without_name_id_policy(self, idp_conf, sp_conf):
        """
        Performs a complete test for the module. The flow should be accepted.
        """
        context, samlfrontend = self.setup_for_authn_req(idp_conf, sp_conf, "")
        _, internal_req = samlfrontend.handle_authn_request(context, BINDING_HTTP_REDIRECT)
        assert internal_req.requestor == sp_conf["entityid"]

        auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", "unittest_idp.xml")
        internal_response = InternalResponse(auth_info=auth_info)
        internal_response.set_user_id_hash_type(internal_req.user_id_hash_type)
        internal_response.add_attributes(USERS["testuser1"])

        resp = samlfrontend.handle_authn_response(context, internal_response)
        resp_dict = parse_qs(urlparse(resp.message).query)

        fakesp = FakeSP(None, config=SPConfig().load(sp_conf, metadata_construction=False))
        resp = fakesp.parse_authn_request_response(resp_dict['SAMLResponse'][0],
                                                   BINDING_HTTP_REDIRECT)
        for key in resp.ava:
            assert USERS["testuser1"][key] == resp.ava[key]
Example #33
0
def test_set_user_id():
    uid = "my_id"
    attributes = {"attr_1": "v1", "attr_2": "v2", "attr_3": "v3"}
    internal_response = InternalResponse(UserIdHashType.persistent)
    internal_response.add_attributes(attributes)
    internal_response.set_user_id(uid)
    assert uid == internal_response.get_user_id()
Example #34
0
    def test_handle_authn_request_without_name_id_policy(
            self, idp_conf, sp_conf):
        """
        Performs a complete test for the module. The flow should be accepted.
        """
        context, samlfrontend = self.setup_for_authn_req(idp_conf, sp_conf, "")
        _, internal_req = samlfrontend.handle_authn_request(
            context, BINDING_HTTP_REDIRECT)
        assert internal_req.requestor == sp_conf["entityid"]

        auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z",
                                              "unittest_idp.xml")
        internal_response = InternalResponse(auth_info=auth_info)
        internal_response.set_user_id_hash_type(internal_req.user_id_hash_type)
        internal_response.add_attributes(USERS["testuser1"])

        resp = samlfrontend.handle_authn_response(context, internal_response)
        resp_dict = parse_qs(urlparse(resp.message).query)

        fakesp = FakeSP(None,
                        config=SPConfig().load(sp_conf,
                                               metadata_construction=False))
        resp = fakesp.parse_authn_request_response(
            resp_dict['SAMLResponse'][0], BINDING_HTTP_REDIRECT)
        for key in resp.ava:
            assert USERS["testuser1"][key] == resp.ava[key]
Example #35
0
    def _handle_al_response(self, context):
        """
        Endpoint for handling account linking service response

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

        :param context: The current context
        :return: response
        """
        saved_state = context.state.get(AccountLinkingModule.STATE_KEY)
        internal_response = InternalResponse.from_dict(saved_state)
        return self.manage_al(context, internal_response)
Example #36
0
    def _handle_al_response(self, context):
        """
        Endpoint for handling account linking service response

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

        :param context: The current context
        :return: response
        """
        saved_state = context.state.get(AccountLinkingModule.STATE_KEY)
        internal_response = InternalResponse.from_dict(saved_state)
        return self.manage_al(context, internal_response)
Example #37
0
    def test_respect_sp_entity_categories(self, entity_category, expected_attributes, idp_conf, sp_conf):
        base = self.construct_base_url_from_entity_id(idp_conf["entityid"])
        conf = {"idp_config": idp_conf, "endpoints": ENDPOINTS, "base": base,
                "state_id": "state_id"}

        internal_attributes = {attr_name: {"saml": [attr_name.lower()]} for attr_name in expected_attributes}
        samlfrontend = SamlFrontend(None, dict(attributes=internal_attributes), conf)
        samlfrontend.register_endpoints(["foo"])

        idp_metadata_str = create_metadata_from_config_dict(samlfrontend.idp_config)
        sp_conf["metadata"]["inline"].append(idp_metadata_str)
        sp_conf["entity_category"] = entity_category
        fakesp = FakeSP(None, config=SPConfig().load(sp_conf, metadata_construction=False))

        auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", idp_conf["entityid"])
        internal_response = InternalResponse(auth_info=auth_info)

        user_attributes = {k: "foo" for k in expected_attributes}
        user_attributes.update({k: "bar" for k in ["extra", "more", "stuff"]})
        internal_response.add_attributes(user_attributes)
        context = Context()
        context.state = State()

        resp_args = {
            "name_id_policy": NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT),
            "in_response_to": None,
            "destination": "",
            "sp_entity_id": None,
            "binding": BINDING_HTTP_REDIRECT
        }
        request_state = samlfrontend.save_state(context, resp_args, "")
        context.state.add(conf["state_id"], request_state)

        resp = samlfrontend.handle_authn_response(context, internal_response)
        resp_dict = parse_qs(urlparse(resp.message).query)
        resp = fakesp.parse_authn_request_response(resp_dict['SAMLResponse'][0],
                                                   BINDING_HTTP_REDIRECT)

        assert Counter(resp.ava.keys()) == Counter(expected_attributes)
Example #38
0
def internal_response():
    auth_info = AuthenticationInformation("auth_class_ref", "timestamp", "issuer")
    internal_response = InternalResponse(auth_info=auth_info)
    internal_response.set_user_id_hash_type(UserIdHashType.persistent)
    internal_response.add_attributes(
            {"displayName": "Test", "co": "example", "sn": "removed_by_filter"})
    internal_response.user_id = "usrID"
    return internal_response
Example #39
0
    def test_acr_mapping_per_idp_in_authn_response(self, idp_conf, sp_conf):
        expected_loa = "LoA1"
        loa = {
            "": "http://eidas.europa.eu/LoA/low",
            idp_conf["entityid"]: expected_loa
        }

        base = self.construct_base_url_from_entity_id(idp_conf["entityid"])
        conf = {
            "idp_config": idp_conf,
            "endpoints": ENDPOINTS,
            "base": base,
            "state_id": "state_id",
            "acr_mapping": loa
        }

        samlfrontend = SamlFrontend(None, INTERNAL_ATTRIBUTES, conf)
        samlfrontend.register_endpoints(["foo"])

        idp_metadata_str = create_metadata_from_config_dict(
            samlfrontend.config)
        sp_conf["metadata"]["inline"].append(idp_metadata_str)
        fakesp = FakeSP(None,
                        config=SPConfig().load(sp_conf,
                                               metadata_construction=False))

        auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z",
                                              idp_conf["entityid"])
        internal_response = InternalResponse(auth_info=auth_info)
        context = Context()
        context.state = State()

        resp_args = {
            "name_id_policy": NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT),
            "in_response_to": None,
            "destination": "",
            "sp_entity_id": None,
            "binding": BINDING_HTTP_REDIRECT
        }
        request_state = samlfrontend.save_state(context, resp_args, "")
        context.state.add(conf["state_id"], request_state)

        resp = samlfrontend.handle_authn_response(context, internal_response)
        resp_dict = parse_qs(urlparse(resp.message).query)
        resp = fakesp.parse_authn_request_response(
            resp_dict['SAMLResponse'][0], BINDING_HTTP_REDIRECT)

        assert len(resp.assertion.authn_statement) == 1
        authn_context_class_ref = resp.assertion.authn_statement[
            0].authn_context.authn_context_class_ref
        assert authn_context_class_ref.text == expected_loa
Example #40
0
def internal_response():
    auth_info = AuthenticationInformation("auth_class_ref", "timestamp",
                                          "issuer")
    internal_response = InternalResponse(auth_info=auth_info)
    internal_response.set_user_id_hash_type(UserIdHashType.persistent)
    internal_response.add_attributes({
        "displayName": "Test",
        "co": "example",
        "sn": "removed_by_filter"
    })
    internal_response.user_id = "usrID"
    return internal_response
Example #41
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[self.name]
        saved_resp = consent_state['internal_response']
        internal_response = InternalResponse.from_dict(saved_resp)
        del context.state[self.name]
        return super().process(context, internal_response)
Example #42
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[consent.STATE_KEY]
        saved_resp = consent_state['internal_response']
        internal_response = InternalResponse.from_dict(saved_resp)
        del context.state[consent.STATE_KEY]
        return super().process(context, internal_response)
Example #43
0
    def test_auth_resp_callback_func_hashes_all_specified_attributes(
            self, context, satosa_config):
        satosa_config["INTERNAL_ATTRIBUTES"]["hash"] = ["user_id", "mail"]
        base = SATOSABase(satosa_config)

        attributes = {
            "user_id": ["user"],
            "mail": ["*****@*****.**", "*****@*****.**"]
        }
        internal_resp = InternalResponse(AuthenticationInformation("", "", ""))
        internal_resp.attributes = copy.copy(attributes)
        internal_resp.user_id = "test_user"
        UserIdHasher.save_state(InternalRequest(UserIdHashType.transient, ""),
                                context.state)
        context.state[satosa.base.STATE_KEY] = {"requester": "test_requester"}
        context.state[satosa.routing.
                      STATE_KEY] = satosa_config["FRONTEND_MODULES"][0]["name"]

        base._auth_resp_callback_func(context, internal_resp)
        for attr in satosa_config["INTERNAL_ATTRIBUTES"]["hash"]:
            assert internal_resp.attributes[attr] == [
                UserIdHasher.hash_data(satosa_config["USER_ID_HASH_SALT"], v)
                for v in attributes[attr]
            ]
Example #44
0
    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
Example #45
0
    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
Example #46
0
    def _handle_consent_response(self, context):
        """
        Endpoint for handling consent service response
        :type context: satosa.context.Context
        :rtype: satosa.response.Response

        :param context: response context
        :return: response
        """
        # Handle answer from consent service
        state = context.state
        consent_state = state.get(ConsentModule.STATE_KEY)
        saved_resp = consent_state["internal_resp"]

        # rebuild internal_response from state
        internal_response = InternalResponse.from_dict(saved_resp)

        requestor = internal_response.to_requestor

        hash_id = self._get_consent_id(requestor, internal_response.get_user_id(),
                                       internal_response.get_attributes())

        try:
            consent_attributes = self._verify_consent(hash_id)
        except ConnectionError:
            satosa_logging(LOGGER, logging.ERROR,
                           "Consent service is not reachable, no consent given.", state)
            # Send an internal_response without any attributes
            consent_attributes = None

        if consent_attributes is None:
            satosa_logging(LOGGER, logging.INFO, "Consent was NOT given", state)
            # If consent was not given, then don't send any attributes
            consent_attributes = []
        else:
            satosa_logging(LOGGER, logging.INFO, "Consent was given", state)

        internal_response = self._filter_attributes(internal_response, consent_attributes)
        return self._end_consent(context, internal_response)
 def internal_response(self):
     auth_info = AuthenticationInformation("auth_class_ref", "timestamp", "issuer")
     internal_response = InternalResponse(auth_info=auth_info)
     internal_response.user_id = "user1"
     return internal_response
Example #48
0
class TestAccountLinking():
    @pytest.fixture(autouse=True)
    def setup(self, signing_key_path):
        self.account_linking_config = {
            "enable": True,
            "rest_uri": "https://localhost:8167",
            "redirect": "https://localhost:8167/approve",
            "endpoint": "handle_account_linking",
            "sign_key": signing_key_path,
            "verify_ssl": False
        }
        self.satosa_config = {
            "BASE": "https://proxy.example.com",
            "USER_ID_HASH_SALT": "qwerty",
            "COOKIE_STATE_NAME": "SATOSA_SATE",
            "STATE_ENCRYPTION_KEY": "ASDasd123",
            "PLUGIN_PATH": "",
            "BACKEND_MODULES": "",
            "FRONTEND_MODULES": "",
            "INTERNAL_ATTRIBUTES": {},
            "ACCOUNT_LINKING": self.account_linking_config
        }
        self.callback_func = MagicMock()
        self.context = Context()
        state = State()
        self.context.state = state
        auth_info = AuthenticationInformation("auth_class_ref", "timestamp",
                                              "issuer")
        self.internal_response = InternalResponse(auth_info=auth_info)

    def test_disable_account_linking(self):
        self.account_linking_config['enable'] = False
        config = SATOSAConfig(self.satosa_config)
        account_linking = AccountLinkingModule(config, self.callback_func)
        account_linking.manage_al(None, None)
        assert self.callback_func.called

    @responses.activate
    def test_store_existing_uuid_in_internal_attributes(self):
        uuid = "uuid"
        responses.add(responses.GET,
                      "%s/get_id" % self.account_linking_config['rest_uri'],
                      status=200,
                      body=uuid,
                      content_type='text/html')
        account_linking = AccountLinkingModule(
            SATOSAConfig(self.satosa_config), self.callback_func)
        account_linking.manage_al(self.context, self.internal_response)
        assert self.internal_response.get_user_id() == uuid

    @responses.activate
    def test_account_link_does_not_exists(self):
        ticket = "ticket"
        responses.add(responses.GET,
                      "%s/get_id" % self.account_linking_config['rest_uri'],
                      status=404,
                      body=ticket,
                      content_type='text/html')
        account_linking = AccountLinkingModule(
            SATOSAConfig(self.satosa_config), self.callback_func)
        result = account_linking.manage_al(self.context,
                                           self.internal_response)
        assert isinstance(result, Redirect)
        assert self.account_linking_config["redirect"] in result.message

    @responses.activate
    def test_handle_failed_connection(self):
        exception = requests.ConnectionError("No connection")
        responses.add(responses.GET,
                      "%s/get_id" % self.account_linking_config['rest_uri'],
                      body=exception)
        account_linking = AccountLinkingModule(
            SATOSAConfig(self.satosa_config), self.callback_func)

        with pytest.raises(SATOSAAuthenticationError):
            account_linking.manage_al(self.context, self.internal_response)
Example #49
0
 def internal_resp(self):
     resp = InternalResponse(AuthenticationInformation(None, str(datetime.now()), 'https://idp.example.com'))
     resp.requester = 'client1'
     resp.user_id = 'user1'
     resp.attributes['affiliation'] = ['student']
     return resp
Example #50
0
 def handle_response(self, context):
     auth_info = AuthenticationInformation("test", str(datetime.now()), "test_issuer")
     internal_resp = InternalResponse(auth_info=auth_info)
     internal_resp.attributes = context.request
     internal_resp.user_id = "test_user"
     return self.auth_callback_func(context, internal_resp)
Example #51
0
 def internal_response(self, idp_conf):
     auth_info = AuthenticationInformation(PASSWORD, "2015-09-30T12:21:37Z", idp_conf["entityid"])
     internal_response = InternalResponse(auth_info=auth_info)
     internal_response.attributes = AttributeMapper(INTERNAL_ATTRIBUTES).to_internal("saml", USERS["testuser1"])
     return internal_response
Example #52
0
class TestAccountLinking():
    @pytest.fixture(autouse=True)
    def setup(self, signing_key_path):
        self.account_linking_config = {
            "enable": True,
            "rest_uri": "https://localhost:8167",
            "redirect": "https://localhost:8167/approve",
            "endpoint": "handle_account_linking",
            "sign_key": signing_key_path,
            "verify_ssl": False
        }
        self.satosa_config = {
            "BASE": "https://proxy.example.com",
            "USER_ID_HASH_SALT": "qwerty",
            "COOKIE_STATE_NAME": "SATOSA_SATE",
            "STATE_ENCRYPTION_KEY": "ASDasd123",
            "PLUGIN_PATH": "",
            "BACKEND_MODULES": "",
            "FRONTEND_MODULES": "",
            "INTERNAL_ATTRIBUTES": {},
            "ACCOUNT_LINKING": self.account_linking_config
        }
        self.callback_func = MagicMock()
        self.context = Context()
        state = State()
        self.context.state = state
        auth_info = AuthenticationInformation("auth_class_ref", "timestamp", "issuer")
        self.internal_response = InternalResponse(auth_info=auth_info)

    def test_disable_account_linking(self):
        self.account_linking_config['enable'] = False
        config = SATOSAConfig(self.satosa_config)
        account_linking = AccountLinkingModule(config, self.callback_func)
        account_linking.manage_al(None, None)
        assert self.callback_func.called

    @responses.activate
    def test_store_existing_uuid_in_internal_attributes(self):
        uuid = "uuid"
        responses.add(
                responses.GET,
                "%s/get_id" % self.account_linking_config['rest_uri'],
                status=200,
                body=uuid,
                content_type='text/html'
        )
        account_linking = AccountLinkingModule(
                SATOSAConfig(self.satosa_config),
                self.callback_func
        )
        account_linking.manage_al(self.context, self.internal_response)
        assert self.internal_response.get_user_id() == uuid

    @responses.activate
    def test_account_link_does_not_exists(self):
        ticket = "ticket"
        responses.add(
                responses.GET,
                "%s/get_id" % self.account_linking_config['rest_uri'],
                status=404,
                body=ticket,
                content_type='text/html'
        )
        account_linking = AccountLinkingModule(
                SATOSAConfig(self.satosa_config),
                self.callback_func
        )
        result = account_linking.manage_al(self.context, self.internal_response)
        assert isinstance(result, Redirect)
        assert self.account_linking_config["redirect"] in result.message

    @responses.activate
    def test_handle_failed_connection(self):
        exception = requests.ConnectionError("No connection")
        responses.add(responses.GET, "%s/get_id" % self.account_linking_config['rest_uri'],
                      body=exception)
        account_linking = AccountLinkingModule(
                SATOSAConfig(self.satosa_config),
                self.callback_func
        )

        with pytest.raises(SATOSAAuthenticationError):
            account_linking.manage_al(self.context, self.internal_response)
Example #53
0
 def internal_response(self):
     auth_info = AuthenticationInformation("auth_class_ref", "timestamp", "issuer")
     internal_response = InternalResponse(auth_info=auth_info)
     internal_response.requester = "client"
     internal_response.attributes = ATTRIBUTES
     return internal_response