neiam
/apollos_crib
It's Apollo's Crib
public · Issues · Pulls 1 · Labels · Forks · Compare · Actions failure · Badges · Packages
⭐
Log in to mark this repository.
Queries: the query list only edits and deletes the user's own
c056659 · Gabe Morell · 2026-11-04 21:59
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