def content(self):
        """Content of the panel when it's displayed in full screen.

        Fetch every store for the toolbar and include it in the template.
        """
        stores = OrderedDict()
        for id, toolbar in reversed(self.toolbar._store.items()):
            stores[id] = {
                "toolbar":
                toolbar,
                "form":
                SignedDataForm(initial=HistoryStoreForm(initial={
                    "store_id": id
                }).initial),
            }

        return render_to_string(
            self.template,
            {
                "current_store_id":
                self.toolbar.store_id,
                "stores":
                stores,
                "refresh_form":
                SignedDataForm(initial=HistoryStoreForm(
                    initial={
                        "store_id": self.toolbar.store_id
                    }).initial),
            },
        )
示例#2
0
    def inner(request, *args, **kwargs):
        from debug_toolbar.forms import SignedDataForm

        data = request.GET if request.method == "GET" else request.POST
        signed_form = SignedDataForm(data)
        if signed_form.is_valid():
            return view(request,
                        *args,
                        verified_data=signed_form.verified_data(),
                        **kwargs)
        return HttpResponseBadRequest("Invalid signature")
示例#3
0
 def test_verified_data(self):
     form = SignedDataForm(data={"signed": SignedDataForm.sign(DATA)})
     self.assertEqual(
         form.verified_data(),
         {
             "value": "foo",
             "date": "2020-01-01 00:00:00+00:00",
         },
     )
     # Take it back to the foo form to validate the datetime is serialized
     foo_form = FooForm(data=form.verified_data())
     self.assertTrue(foo_form.is_valid())
     self.assertDictEqual(foo_form.cleaned_data, DATA)
    def test_history_sidebar_invalid(self):
        response = self.client.get(reverse("djdt:history_sidebar"))
        self.assertEqual(response.status_code, 400)

        data = {"signed": SignedDataForm.sign({"store_id": "foo"}) + "invalid"}
        response = self.client.get(reverse("djdt:history_sidebar"), data=data)
        self.assertEqual(response.status_code, 400)
示例#5
0
    def test_sql_profile_checks_show_toolbar(self):
        url = "/__debug__/sql_profile/"
        data = {
            "signed":
            SignedDataForm.sign({
                "sql": "SELECT * FROM auth_user",
                "raw_sql": "SELECT * FROM auth_user",
                "params": "{}",
                "alias": "default",
                "duration": "0",
            })
        }

        response = self.client.post(url, data)
        self.assertEqual(response.status_code, 200)
        response = self.client.post(url,
                                    data,
                                    HTTP_X_REQUESTED_WITH="XMLHttpRequest")
        self.assertEqual(response.status_code, 200)
        with self.settings(INTERNAL_IPS=[]):
            response = self.client.post(url, data)
            self.assertEqual(response.status_code, 404)
            response = self.client.post(url,
                                        data,
                                        HTTP_X_REQUESTED_WITH="XMLHttpRequest")
            self.assertEqual(response.status_code, 404)
示例#6
0
 def test_sql_explain_postgres_json_field(self):
     url = "/__debug__/sql_explain/"
     base_query = (
         'SELECT * FROM "tests_postgresjson" WHERE "tests_postgresjson"."field" @>'
     )
     query = base_query + """ '{"foo": "bar"}'"""
     data = {
         "signed":
         SignedDataForm.sign({
             "sql": query,
             "raw_sql": base_query + " %s",
             "params": '["{\\"foo\\": \\"bar\\"}"]',
             "alias": "default",
             "duration": "0",
         })
     }
     response = self.client.post(url, data)
     self.assertEqual(response.status_code, 200)
     response = self.client.post(url,
                                 data,
                                 HTTP_X_REQUESTED_WITH="XMLHttpRequest")
     self.assertEqual(response.status_code, 200)
     with self.settings(INTERNAL_IPS=[]):
         response = self.client.post(url, data)
         self.assertEqual(response.status_code, 404)
         response = self.client.post(url,
                                     data,
                                     HTTP_X_REQUESTED_WITH="XMLHttpRequest")
         self.assertEqual(response.status_code, 404)
示例#7
0
def history_refresh(request, verified_data):
    """Returns the refreshed list of table rows for the History Panel."""
    form = HistoryStoreForm(verified_data)

    if form.is_valid():
        requests = []
        # Convert to list to handle mutations happenening in parallel
        for id, toolbar in list(DebugToolbar._store.items())[::-1]:
            requests.append({
                "id":
                id,
                "content":
                render_to_string(
                    "debug_toolbar/panels/history_tr.html",
                    {
                        "id": id,
                        "store_context": {
                            "toolbar":
                            toolbar,
                            "form":
                            SignedDataForm(initial=HistoryStoreForm(
                                initial={
                                    "store_id": id
                                }).initial),
                        },
                    },
                ),
            })

        return JsonResponse({"requests": requests})
    return HttpResponseBadRequest("Form errors")
 def test_history_refresh(self):
     """Verify refresh history response has request variables."""
     data = {"foo": "bar"}
     self.client.get("/json_view/", data, content_type="application/json")
     data = {"signed": SignedDataForm.sign({"store_id": "foo"})}
     response = self.client.get(reverse("djdt:history_refresh"), data=data)
     self.assertEqual(response.status_code, 200)
     data = response.json()
     self.assertEqual(len(data["requests"]), 1)
     for val in ["foo", "bar"]:
         self.assertIn(val, data["requests"][0]["content"])
 def test_history_sidebar(self):
     """Validate the history sidebar view."""
     self.client.get("/json_view/")
     store_id = list(DebugToolbar._store.keys())[0]
     data = {"signed": SignedDataForm.sign({"store_id": store_id})}
     response = self.client.get(reverse("djdt:history_sidebar"), data=data)
     self.assertEqual(response.status_code, 200)
     self.assertEqual(
         set(response.json().keys()),
         {
             "VersionsPanel",
             "TimerPanel",
             "SettingsPanel",
             "HeadersPanel",
             "RequestPanel",
             "SQLPanel",
             "StaticFilesPanel",
             "TemplatesPanel",
             "CachePanel",
             "SignalsPanel",
             "LoggingPanel",
             "ProfilingPanel",
         },
     )
示例#10
0
    def generate_stats(self, request, response):
        colors = contrasting_color_generator()
        trace_colors = defaultdict(lambda: next(colors))
        query_similar = defaultdict(lambda: defaultdict(int))
        query_duplicates = defaultdict(lambda: defaultdict(int))

        # The keys used to determine similar and duplicate queries.
        def similar_key(query):
            return query["raw_sql"]

        def duplicate_key(query):
            raw_params = (
                () if query["raw_params"] is None else tuple(query["raw_params"])
            )
            # saferepr() avoids problems because of unhashable types
            # (e.g. lists) when used as dictionary keys.
            # https://github.com/jazzband/django-debug-toolbar/issues/1091
            return (query["raw_sql"], saferepr(raw_params))

        if self._queries:
            width_ratio_tally = 0
            factor = int(256.0 / (len(self._databases) * 2.5))
            for n, db in enumerate(self._databases.values()):
                rgb = [0, 0, 0]
                color = n % 3
                rgb[color] = 256 - n // 3 * factor
                nn = color
                # XXX: pretty sure this is horrible after so many aliases
                while rgb[color] < factor:
                    nc = min(256 - rgb[color], 256)
                    rgb[color] += nc
                    nn += 1
                    if nn > 2:
                        nn = 0
                    rgb[nn] = nc
                db["rgb_color"] = rgb

            trans_ids = {}
            trans_id = None
            i = 0
            for alias, query in self._queries:
                query_similar[alias][similar_key(query)] += 1
                query_duplicates[alias][duplicate_key(query)] += 1

                trans_id = query.get("trans_id")
                last_trans_id = trans_ids.get(alias)

                if trans_id != last_trans_id:
                    if last_trans_id:
                        self._queries[(i - 1)][1]["ends_trans"] = True
                    trans_ids[alias] = trans_id
                    if trans_id:
                        query["starts_trans"] = True
                if trans_id:
                    query["in_trans"] = True

                query["alias"] = alias
                if "iso_level" in query:
                    query["iso_level"] = get_isolation_level_display(
                        query["vendor"], query["iso_level"]
                    )
                if "trans_status" in query:
                    query["trans_status"] = get_transaction_status_display(
                        query["vendor"], query["trans_status"]
                    )

                query["form"] = SignedDataForm(
                    auto_id=None, initial=SQLSelectForm(initial=copy(query)).initial
                )

                if query["sql"]:
                    query["sql"] = reformat_sql(query["sql"], with_toggle=True)
                query["rgb_color"] = self._databases[alias]["rgb_color"]
                try:
                    query["width_ratio"] = (query["duration"] / self._sql_time) * 100
                except ZeroDivisionError:
                    query["width_ratio"] = 0
                query["start_offset"] = width_ratio_tally
                query["end_offset"] = query["width_ratio"] + query["start_offset"]
                width_ratio_tally += query["width_ratio"]
                query["stacktrace"] = render_stacktrace(query["stacktrace"])
                i += 1

                query["trace_color"] = trace_colors[query["stacktrace"]]

            if trans_id:
                self._queries[(i - 1)][1]["ends_trans"] = True

        # Queries are similar / duplicates only if there's as least 2 of them.
        # Also, to hide queries, we need to give all the duplicate groups an id
        query_colors = contrasting_color_generator()
        query_similar_colors = {
            alias: {
                query: (similar_count, next(query_colors))
                for query, similar_count in queries.items()
                if similar_count >= 2
            }
            for alias, queries in query_similar.items()
        }
        query_duplicates_colors = {
            alias: {
                query: (duplicate_count, next(query_colors))
                for query, duplicate_count in queries.items()
                if duplicate_count >= 2
            }
            for alias, queries in query_duplicates.items()
        }

        for alias, query in self._queries:
            try:
                (query["similar_count"], query["similar_color"]) = query_similar_colors[
                    alias
                ][similar_key(query)]
                (
                    query["duplicate_count"],
                    query["duplicate_color"],
                ) = query_duplicates_colors[alias][duplicate_key(query)]
            except KeyError:
                pass

        for alias, alias_info in self._databases.items():
            try:
                alias_info["similar_count"] = sum(
                    e[0] for e in query_similar_colors[alias].values()
                )
                alias_info["duplicate_count"] = sum(
                    e[0] for e in query_duplicates_colors[alias].values()
                )
            except KeyError:
                pass

        self.record_stats(
            {
                "databases": sorted(
                    self._databases.items(), key=lambda x: -x[1]["time_spent"]
                ),
                "queries": [q for a, q in self._queries],
                "sql_time": self._sql_time,
            }
        )
示例#11
0
 def test_prevents_tampering(self):
     data = {
         "signed": SIGNED_DATA.replace('"value": "foo"', '"value": "bar"')
     }
     form = SignedDataForm(data=data)
     self.assertFalse(form.is_valid())
示例#12
0
 def test_initial_set_signed(self):
     form = SignedDataForm(initial=DATA)
     self.assertEqual(form.initial["signed"], SIGNED_DATA)
示例#13
0
 def test_signed_data(self):
     data = {"signed": SignedDataForm.sign(DATA)}
     form = SignedDataForm(data=data)
     self.assertTrue(form.is_valid())
     # Check the signature value
     self.assertEqual(data["signed"], SIGNED_DATA)
示例#14
0
    def generate_stats(self, request, response):
        colors = contrasting_color_generator()
        trace_colors = defaultdict(lambda: next(colors))
        similar_query_groups = defaultdict(list)
        duplicate_query_groups = defaultdict(list)

        if self._queries:
            width_ratio_tally = 0
            factor = int(256.0 / (len(self._databases) * 2.5))
            for n, db in enumerate(self._databases.values()):
                rgb = [0, 0, 0]
                color = n % 3
                rgb[color] = 256 - n // 3 * factor
                nn = color
                # XXX: pretty sure this is horrible after so many aliases
                while rgb[color] < factor:
                    nc = min(256 - rgb[color], 256)
                    rgb[color] += nc
                    nn += 1
                    if nn > 2:
                        nn = 0
                    rgb[nn] = nc
                db["rgb_color"] = rgb

            # the last query recorded for each DB alias
            last_by_alias = {}
            for query in self._queries:
                alias = query["alias"]

                similar_query_groups[(alias,
                                      _similar_query_key(query))].append(query)
                duplicate_query_groups[(
                    alias, _duplicate_query_key(query))].append(query)

                trans_id = query.get("trans_id")
                prev_query = last_by_alias.get(alias, {})
                prev_trans_id = prev_query.get("trans_id")

                # If two consecutive queries for a given DB alias have different
                # transaction ID values, a transaction started, finished, or both, so
                # annotate the queries as appropriate.
                if trans_id != prev_trans_id:
                    if prev_trans_id is not None:
                        prev_query["ends_trans"] = True
                    if trans_id is not None:
                        query["starts_trans"] = True
                if trans_id is not None:
                    query["in_trans"] = True

                if "iso_level" in query:
                    query["iso_level"] = get_isolation_level_display(
                        query["vendor"], query["iso_level"])
                if "trans_status" in query:
                    query["trans_status"] = get_transaction_status_display(
                        query["vendor"], query["trans_status"])

                query["form"] = SignedDataForm(
                    auto_id=None,
                    initial=SQLSelectForm(initial=copy(query)).initial)

                if query["sql"]:
                    query["sql"] = reformat_sql(query["sql"], with_toggle=True)
                query["rgb_color"] = self._databases[alias]["rgb_color"]
                try:
                    query["width_ratio"] = (query["duration"] /
                                            self._sql_time) * 100
                except ZeroDivisionError:
                    query["width_ratio"] = 0
                query["start_offset"] = width_ratio_tally
                query["end_offset"] = query["width_ratio"] + query[
                    "start_offset"]
                width_ratio_tally += query["width_ratio"]
                query["stacktrace"] = render_stacktrace(query["stacktrace"])

                query["trace_color"] = trace_colors[query["stacktrace"]]

                last_by_alias[alias] = query

            # Close out any transactions that were in progress, since there is no
            # explicit way to know when a transaction finishes.
            for final_query in last_by_alias.values():
                if final_query.get("trans_id") is not None:
                    final_query["ends_trans"] = True

        group_colors = contrasting_color_generator()
        _process_query_groups(similar_query_groups, self._databases,
                              group_colors, "similar")
        _process_query_groups(duplicate_query_groups, self._databases,
                              group_colors, "duplicate")

        self.record_stats({
            "databases":
            sorted(self._databases.items(), key=lambda x: -x[1]["time_spent"]),
            "queries":
            self._queries,
            "sql_time":
            self._sql_time,
        })