diff --git a/sentry_sdk/integrations/tornado.py b/sentry_sdk/integrations/tornado.py index 836f3a4b25..e71bf94d12 100644 --- a/sentry_sdk/integrations/tornado.py +++ b/sentry_sdk/integrations/tornado.py @@ -133,8 +133,16 @@ def _handle_request_impl(self: "RequestHandler") -> "Generator[None, None, None] sentry_sdk.traces.continue_trace(dict(headers)) scope.set_custom_sampling_context({"tornado_request": self.request}) - if should_send_default_pii() and self.request.remote_ip: - scope.set_attribute(SPANDATA.USER_IP_ADDRESS, self.request.remote_ip) + if self.request.remote_ip: + if has_data_collection_enabled(client.options): + if client.options["data_collection"]["user_info"]: + scope.set_attribute( + SPANDATA.USER_IP_ADDRESS, self.request.remote_ip + ) + elif should_send_default_pii(): + scope.set_attribute( + SPANDATA.USER_IP_ADDRESS, self.request.remote_ip + ) span_ctx = sentry_sdk.traces.start_span( name=_DEFAULT_ROOT_SPAN_NAME, @@ -218,6 +226,10 @@ def _get_request_attributes(request: "Any") -> "Dict[str, Any]": f"{parsed_url.url}?{filtered_query}" if filtered_query else parsed_url.url ) + if request.remote_ip: + if client_options["data_collection"]["user_info"]: + attributes[SPANDATA.CLIENT_ADDRESS] = request.remote_ip + elif should_send_default_pii(): attributes[SPANDATA.URL_FULL] = request.full_url() attributes["url.path"] = request.path @@ -225,12 +237,12 @@ def _get_request_attributes(request: "Any") -> "Dict[str, Any]": if request.query: attributes[SPANDATA.URL_QUERY] = request.query + if request.remote_ip: + attributes[SPANDATA.CLIENT_ADDRESS] = request.remote_ip + if request.protocol: attributes[SPANDATA.NETWORK_PROTOCOL_NAME] = request.protocol - if should_send_default_pii() and request.remote_ip: - attributes[SPANDATA.CLIENT_ADDRESS] = request.remote_ip - with capture_internal_exceptions(): raw_data = _get_tornado_request_data(request) body_data = raw_data.value if isinstance(raw_data, AnnotatedValue) else raw_data @@ -282,6 +294,7 @@ def tornado_processor(event: "Event", hint: "dict[str, Any]") -> "Event": event["transaction"] = transaction_from_function(method) or "" event["transaction_info"] = {"source": TransactionSource.COMPONENT} + client_options = sentry_sdk.get_client().options with capture_internal_exceptions(): extractor = TornadoRequestExtractor(request) extractor.extract_into_event(event) @@ -294,7 +307,6 @@ def tornado_processor(event: "Event", hint: "dict[str, Any]") -> "Event": request.path, ) - client_options = sentry_sdk.get_client().options if has_data_collection_enabled(client_options): if request.query: filtered_query = _apply_data_collection_filtering_to_query_string( @@ -310,7 +322,16 @@ def tornado_processor(event: "Event", hint: "dict[str, Any]") -> "Event": request_info["env"] = {"REMOTE_ADDR": request.remote_ip} request_info["headers"] = _filter_headers(dict(request.headers)) - if should_send_default_pii(): + if has_data_collection_enabled(client_options): + if client_options["data_collection"]["user_info"]: + try: + current_user = handler.current_user + except Exception: + current_user = None + + if current_user: + event.setdefault("user", {}).setdefault("is_authenticated", True) + elif should_send_default_pii(): try: current_user = handler.current_user except Exception: diff --git a/tests/integrations/tornado/test_tornado.py b/tests/integrations/tornado/test_tornado.py index 23fbc316d6..2268a791ee 100644 --- a/tests/integrations/tornado/test_tornado.py +++ b/tests/integrations/tornado/test_tornado.py @@ -8,6 +8,7 @@ from sentry_sdk import capture_message, start_transaction from sentry_sdk._types import SENSITIVE_DATA_SUBSTITUTE from sentry_sdk.integrations.tornado import TornadoIntegration +from tests.integrations.utils import DATA_COLLECTION_USER_INFO_CASES @pytest.fixture @@ -701,6 +702,32 @@ def get(self): assert "user" not in event +@pytest.mark.parametrize("init_kwargs, expect_user", DATA_COLLECTION_USER_INFO_CASES) +def test_user_auth_data_collection( + tornado_testcase, sentry_init, capture_events, init_kwargs, expect_user +): + sentry_init(integrations=[TornadoIntegration()], **init_kwargs) + events = capture_events() + + class UserHandler(RequestHandler): + def get(self): + 1 / 0 + + def get_current_user(self): + return 42 + + client = tornado_testcase(Application([(r"/auth", UserHandler)])) + + response = client.fetch("/auth") + assert response.code == 500 + + (event,) = events + if expect_user: + assert event["user"] == {"is_authenticated": True} + else: + assert "is_authenticated" not in event.get("user", {}) + + def test_formdata(tornado_testcase, sentry_init, capture_events): sentry_init(integrations=[TornadoIntegration()], send_default_pii=True) events = capture_events() @@ -927,15 +954,15 @@ def test_span_origin( assert event["contexts"]["trace"]["origin"] == "auto.http.tornado" -@pytest.mark.parametrize("send_default_pii", [True, False]) +@pytest.mark.parametrize("init_kwargs, expect_ip", DATA_COLLECTION_USER_INFO_CASES) def test_user_ip_address_on_all_spans( - tornado_testcase, sentry_init, capture_items, send_default_pii + tornado_testcase, sentry_init, capture_items, init_kwargs, expect_ip ): sentry_init( integrations=[TornadoIntegration()], traces_sample_rate=1.0, - send_default_pii=send_default_pii, trace_lifecycle="stream", + **init_kwargs, ) items = capture_items("span") @@ -947,9 +974,39 @@ def test_user_ip_address_on_all_spans( child_span, server_span = [item.payload for item in items] - if send_default_pii: + if expect_ip: assert server_span["attributes"]["user.ip_address"] == "127.0.0.1" assert child_span["attributes"]["user.ip_address"] == "127.0.0.1" else: assert "user.ip_address" not in server_span["attributes"] assert "user.ip_address" not in child_span["attributes"] + + +@pytest.mark.parametrize("init_kwargs, expect_ip", DATA_COLLECTION_USER_INFO_CASES) +def test_client_address_span_attribute_data_collection( + tornado_testcase, sentry_init, capture_items, init_kwargs, expect_ip +): + sentry_init( + integrations=[TornadoIntegration()], + traces_sample_rate=1.0, + trace_lifecycle="stream", + **init_kwargs, + ) + + items = capture_items("span") + + client = tornado_testcase(Application([(r"/hi", ChildSpanHandler)])) + client.fetch("/hi") + + sentry_sdk.flush() + + (server_span,) = [ + item.payload + for item in items + if item.payload["attributes"].get("sentry.origin") == "auto.http.tornado" + ] + + if expect_ip: + assert server_span["attributes"]["client.address"] == "127.0.0.1" + else: + assert "client.address" not in server_span["attributes"]