Queries: the query list only edits and deletes the user's own

c056659 · Gabe Morell · 2026-11-04 21:59

11 files +85 -2
Message
{commit_body(@commit)}

Files changed

modified apps/room_sanctum/lib/room_sanctum_web/live/cfg_query_live/index.ex
+3 −2
@@ -71,7 +71,7 @@ defmodule RoomSanctumWeb.QueryLive.Index do
71 71 defp apply_action(socket, :edit, %{"id" => id}) do
72 72 socket
73 73 |> assign(:page_title, "Modify Query")
74 − |> assign(:query, Configuration.get_query!(id))
74 + |> assign(:query, Configuration.get_query!({:user, socket.assigns.current_user.id}, id))
75 75 end
76 76
77 77 defp apply_action(socket, :new, _params) do
@@ -174,8 +174,9 @@ defmodule RoomSanctumWeb.QueryLive.Index do
174 174 end
175 175
176 176 @impl true
177 + # The id is the client's, so it is looked up among the user's own queries.
177 178 def handle_event("delete", %{"id" => id}, socket) do
178 − query = Configuration.get_query!(id)
179 + query = Configuration.get_query!({:user, socket.assigns.current_user.id}, id)
179 180 {:ok, _} = Configuration.delete_query(query)
180 181
181 182 {:noreply, stream_delete(socket, :cfg_queries, query)}
added apps/room_sanctum/test/room_sanctum_web/live/ownership_test.exs
+82 −0
@@ -0,0 +1,82 @@
1 +defmodule RoomSanctumWeb.OwnershipTest do
2 + @moduledoc """
3 + The /cfg pages only reach the signed-in user's own records. Another user's
4 + id -- in the URL, or in an event the client sends -- gets the same
5 + not-found a missing one does, and changes nothing.
6 + """
7 + use RoomSanctumWeb.ConnCase
8 +
9 + import Phoenix.LiveViewTest
10 +
11 + alias RoomSanctum.{Accounts, Configuration, Repo}
12 +
13 + defp user do
14 + {:ok, user} =
15 + Accounts.register_user(%{
16 + email: "own#{System.unique_integer([:positive])}@example.com",
17 + password: "hello world!hello world!"
18 + })
19 +
20 + user
21 + end
22 +
23 + defp source_for(user, name \\ "Markets") do
24 + {:ok, source} =
25 + Configuration.create_source(%{
26 + name: name,
27 + notes: "",
28 + type: :bourse,
29 + enabled: true,
30 + user_id: user.id,
31 + config: %{"__type__" => "bourse"}
32 + })
33 +
34 + source
35 + end
36 +
37 + defp query_for(user) do
38 + source = source_for(user)
39 +
40 + {:ok, query} =
41 + Configuration.create_query(%{
42 + name: "Their query",
43 + notes: "",
44 + source_id: source.id,
45 + user_id: user.id,
46 + query: %{"__type__" => "bourse", "symbol" => "SECRET"}
47 + })
48 +
49 + query
50 + end
51 +
52 + # A lookup that raises takes the LiveView down with it, as a missing id
53 + # always has; the test only cares that nothing was changed on the way.
54 + defp forge(live, event, params) do
55 + Process.flag(:trap_exit, true)
56 + catch_exit(render_click(live, event, params))
57 + end
58 +
59 + setup %{conn: conn} do
60 + me = user()
61 + %{conn: log_in_user(conn, me), me: me, them: user()}
62 + end
63 +
64 + describe "the query list" do
65 + test "will not open another user's query for editing", ctx do
66 + theirs = query_for(ctx.them)
67 +
68 + assert_raise Ecto.NoResultsError, fn ->
69 + live(ctx.conn, Routes.query_index_path(ctx.conn, :edit, theirs))
70 + end
71 + end
72 +
73 + test "will not delete another user's query", ctx do
74 + theirs = query_for(ctx.them)
75 + {:ok, live, _html} = live(ctx.conn, Routes.query_index_path(ctx.conn, :index))
76 +
77 + forge(live, "delete", %{"id" => to_string(theirs.id)})
78 +
79 + assert Repo.reload(theirs)
80 + end
81 + end
82 +end

Parents: b46cd93