diff --git a/bugs.md b/bugs.md index a3dd296e..fe3a71d0 100644 --- a/bugs.md +++ b/bugs.md @@ -59,17 +59,6 @@ ## MEDIUM -### M1. Resource Scoping Reveals Org Membership - -**File:** `lib/towerops_web/scoped_resource.ex:15-20` - -**Severity:** MEDIUM — Cross-org resource existence probing - -**Description:** Returns `:forbidden` for wrong-org resources vs `:not_found` for nonexistent. Organization A can probe if a specific resource ID belongs to Organization B. - -**Fix:** Return `:not_found` in both cases — use `Repo.get_by(schema, id: id, organization_id: organization_id)`. - ---- ### M4. Stripe Webhook Signature Not Enforced at Plug Level diff --git a/lib/towerops_web/controllers/api/v1/checks_controller.ex b/lib/towerops_web/controllers/api/v1/checks_controller.ex index 37df870f..0b11467b 100644 --- a/lib/towerops_web/controllers/api/v1/checks_controller.ex +++ b/lib/towerops_web/controllers/api/v1/checks_controller.ex @@ -110,11 +110,6 @@ defmodule ToweropsWeb.Api.V1.ChecksController do {:ok, check} -> json(conn, format_check(check)) - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this check"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -142,11 +137,6 @@ defmodule ToweropsWeb.Api.V1.ChecksController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this check"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -180,11 +170,6 @@ defmodule ToweropsWeb.Api.V1.ChecksController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this check"}) - {:error, :not_found} -> conn |> put_status(:not_found) diff --git a/lib/towerops_web/controllers/api/v1/devices_controller.ex b/lib/towerops_web/controllers/api/v1/devices_controller.ex index e1263d84..1457170b 100644 --- a/lib/towerops_web/controllers/api/v1/devices_controller.ex +++ b/lib/towerops_web/controllers/api/v1/devices_controller.ex @@ -106,7 +106,7 @@ defmodule ToweropsWeb.Api.V1.DevicesController do {:error, reason} -> conn - |> put_status(:forbidden) + |> put_status(:not_found) |> json(%{error: reason}) end end @@ -144,11 +144,6 @@ defmodule ToweropsWeb.Api.V1.DevicesController do {:ok, device} -> json(conn, format_device_details(device)) - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this device"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -192,11 +187,6 @@ defmodule ToweropsWeb.Api.V1.DevicesController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this device"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -235,11 +225,6 @@ defmodule ToweropsWeb.Api.V1.DevicesController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this device"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -280,7 +265,6 @@ defmodule ToweropsWeb.Api.V1.DevicesController do defp verify_site_access(site_id, organization_id) do case ScopedResource.fetch(Site, site_id, organization_id) do {:ok, _site} -> :ok - {:error, :forbidden} -> {:error, "Access denied to this site"} {:error, :not_found} -> {:error, "Site not found"} end end diff --git a/lib/towerops_web/controllers/api/v1/escalation_policies_controller.ex b/lib/towerops_web/controllers/api/v1/escalation_policies_controller.ex index bf5d7ff5..7e61333b 100644 --- a/lib/towerops_web/controllers/api/v1/escalation_policies_controller.ex +++ b/lib/towerops_web/controllers/api/v1/escalation_policies_controller.ex @@ -67,11 +67,6 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesController do {:ok, policy} -> json(conn, format_policy_detail(policy)) - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this escalation policy"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -95,11 +90,6 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this escalation policy"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -129,11 +119,6 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this escalation policy"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -163,11 +148,6 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this escalation policy"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -191,9 +171,6 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesController do |> json(%{errors: translate_errors(changeset)}) end else - {:error, :forbidden} -> - conn |> put_status(:forbidden) |> json(%{error: "Access denied to this escalation policy"}) - {:error, :not_found} -> conn |> put_status(:not_found) |> json(%{error: "Escalation policy not found"}) @@ -211,9 +188,6 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesController do {:ok, _} = OnCall.delete_escalation_rule(rule) json(conn, %{success: true}) else - {:error, :forbidden} -> - conn |> put_status(:forbidden) |> json(%{error: "Access denied to this escalation policy"}) - {:error, :not_found} -> conn |> put_status(:not_found) |> json(%{error: "Escalation policy not found"}) @@ -244,9 +218,6 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesController do |> json(%{errors: translate_errors(changeset)}) end else - {:error, :forbidden} -> - conn |> put_status(:forbidden) |> json(%{error: "Access denied to this escalation policy"}) - {:error, :not_found} -> conn |> put_status(:not_found) |> json(%{error: "Escalation policy not found"}) @@ -273,9 +244,6 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesController do conn |> put_status(:internal_server_error) |> json(%{error: "Failed to delete escalation target"}) end else - {:error, :forbidden} -> - conn |> put_status(:forbidden) |> json(%{error: "Access denied to this escalation policy"}) - {:error, :not_found} -> conn |> put_status(:not_found) |> json(%{error: "Escalation policy not found"}) diff --git a/lib/towerops_web/controllers/api/v1/maintenance_windows_controller.ex b/lib/towerops_web/controllers/api/v1/maintenance_windows_controller.ex index 17f5cf2b..9ab2ba28 100644 --- a/lib/towerops_web/controllers/api/v1/maintenance_windows_controller.ex +++ b/lib/towerops_web/controllers/api/v1/maintenance_windows_controller.ex @@ -71,11 +71,6 @@ defmodule ToweropsWeb.Api.V1.MaintenanceWindowsController do {:ok, window} -> json(conn, format_window(window)) - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this maintenance window"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -99,11 +94,6 @@ defmodule ToweropsWeb.Api.V1.MaintenanceWindowsController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this maintenance window"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -133,11 +123,6 @@ defmodule ToweropsWeb.Api.V1.MaintenanceWindowsController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this maintenance window"}) - {:error, :not_found} -> conn |> put_status(:not_found) diff --git a/lib/towerops_web/controllers/api/v1/schedules_controller.ex b/lib/towerops_web/controllers/api/v1/schedules_controller.ex index 62f45382..b3da5764 100644 --- a/lib/towerops_web/controllers/api/v1/schedules_controller.ex +++ b/lib/towerops_web/controllers/api/v1/schedules_controller.ex @@ -68,11 +68,6 @@ defmodule ToweropsWeb.Api.V1.SchedulesController do {:ok, schedule} -> json(conn, format_schedule_detail(schedule)) - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this schedule"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -96,11 +91,6 @@ defmodule ToweropsWeb.Api.V1.SchedulesController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this schedule"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -130,11 +120,6 @@ defmodule ToweropsWeb.Api.V1.SchedulesController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this schedule"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -153,11 +138,6 @@ defmodule ToweropsWeb.Api.V1.SchedulesController do user = OnCall.who_is_on_call(id) json(conn, %{on_call: format_user(user)}) - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this schedule"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -187,11 +167,6 @@ defmodule ToweropsWeb.Api.V1.SchedulesController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this schedule"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -215,9 +190,6 @@ defmodule ToweropsWeb.Api.V1.SchedulesController do |> json(%{errors: translate_errors(changeset)}) end else - {:error, :forbidden} -> - conn |> put_status(:forbidden) |> json(%{error: "Access denied to this schedule"}) - {:error, :not_found} -> conn |> put_status(:not_found) |> json(%{error: "Schedule not found"}) @@ -235,9 +207,6 @@ defmodule ToweropsWeb.Api.V1.SchedulesController do {:ok, _} = OnCall.delete_layer(layer) json(conn, %{success: true}) else - {:error, :forbidden} -> - conn |> put_status(:forbidden) |> json(%{error: "Access denied to this schedule"}) - {:error, :not_found} -> conn |> put_status(:not_found) |> json(%{error: "Schedule not found"}) @@ -289,9 +258,6 @@ defmodule ToweropsWeb.Api.V1.SchedulesController do {:ok, _} = OnCall.remove_layer_member(member) json(conn, %{success: true}) else - {:error, :forbidden} -> - conn |> put_status(:forbidden) |> json(%{error: "Access denied to this schedule"}) - {:error, :not_found} -> conn |> put_status(:not_found) |> json(%{error: "Schedule not found"}) @@ -325,9 +291,6 @@ defmodule ToweropsWeb.Api.V1.SchedulesController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn |> put_status(:forbidden) |> json(%{error: "Access denied to this schedule"}) - {:error, :not_found} -> conn |> put_status(:not_found) |> json(%{error: "Schedule not found"}) end @@ -348,9 +311,6 @@ defmodule ToweropsWeb.Api.V1.SchedulesController do conn |> put_status(:internal_server_error) |> json(%{error: "Failed to delete override"}) end else - {:error, :forbidden} -> - conn |> put_status(:forbidden) |> json(%{error: "Access denied to this schedule"}) - {:error, :not_found} -> conn |> put_status(:not_found) |> json(%{error: "Schedule not found"}) diff --git a/lib/towerops_web/controllers/api/v1/sites_controller.ex b/lib/towerops_web/controllers/api/v1/sites_controller.ex index f2036e56..15e9bf69 100644 --- a/lib/towerops_web/controllers/api/v1/sites_controller.ex +++ b/lib/towerops_web/controllers/api/v1/sites_controller.ex @@ -87,11 +87,6 @@ defmodule ToweropsWeb.Api.V1.SitesController do {:ok, site} -> json(conn, format_site(site)) - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this site"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -119,11 +114,6 @@ defmodule ToweropsWeb.Api.V1.SitesController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this site"}) - {:error, :not_found} -> conn |> put_status(:not_found) @@ -157,11 +147,6 @@ defmodule ToweropsWeb.Api.V1.SitesController do |> json(%{errors: translate_errors(changeset)}) end - {:error, :forbidden} -> - conn - |> put_status(:forbidden) - |> json(%{error: "Access denied to this site"}) - {:error, :not_found} -> conn |> put_status(:not_found) diff --git a/lib/towerops_web/scoped_resource.ex b/lib/towerops_web/scoped_resource.ex index b2ae94fb..20f6fa10 100644 --- a/lib/towerops_web/scoped_resource.ex +++ b/lib/towerops_web/scoped_resource.ex @@ -8,15 +8,9 @@ defmodule ToweropsWeb.ScopedResource do @spec fetch(module(), Ecto.UUID.t(), Ecto.UUID.t()) :: {:ok, struct()} | {:error, :not_found | :forbidden} def fetch(schema, id, organization_id) do - case Repo.get(schema, id) do - nil -> - {:error, :not_found} - - %{organization_id: ^organization_id} = resource -> - {:ok, resource} - - %{organization_id: _other_org_id} -> - {:error, :forbidden} + case Repo.get_by(schema, id: id, organization_id: organization_id) do + nil -> {:error, :not_found} + resource -> {:ok, resource} end end diff --git a/test/towerops_web/controllers/api/v1/checks_controller_test.exs b/test/towerops_web/controllers/api/v1/checks_controller_test.exs index 4d25baf3..373f0f8d 100644 --- a/test/towerops_web/controllers/api/v1/checks_controller_test.exs +++ b/test/towerops_web/controllers/api/v1/checks_controller_test.exs @@ -297,14 +297,14 @@ defmodule ToweropsWeb.Api.V1.ChecksControllerTest do assert %{"error" => "Check not found"} = json_response(conn, 404) end - test "returns 403 when check belongs to different organization", %{conn: conn} do + test "returns 404 when check belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_check = create_check(other_org.id) conn = get(conn, ~p"/api/v1/checks/#{other_check.id}") - assert %{"error" => "Access denied to this check"} = json_response(conn, 403) + assert %{"error" => "Check not found"} = json_response(conn, 404) end end @@ -342,7 +342,7 @@ defmodule ToweropsWeb.Api.V1.ChecksControllerTest do assert %{"error" => "Check not found"} = json_response(conn, 404) end - test "returns 403 when check belongs to different organization", %{conn: conn} do + test "returns 404 when check belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_check = create_check(other_org.id) @@ -351,7 +351,7 @@ defmodule ToweropsWeb.Api.V1.ChecksControllerTest do conn = patch(conn, ~p"/api/v1/checks/#{other_check.id}", params) - assert %{"error" => "Access denied to this check"} = json_response(conn, 403) + assert %{"error" => "Check not found"} = json_response(conn, 404) end test "returns 422 with invalid params", %{conn: conn, organization: organization} do @@ -391,14 +391,14 @@ defmodule ToweropsWeb.Api.V1.ChecksControllerTest do assert %{"error" => "Check not found"} = json_response(conn, 404) end - test "returns 403 when check belongs to different organization", %{conn: conn} do + test "returns 404 when check belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_check = create_check(other_org.id) conn = delete(conn, ~p"/api/v1/checks/#{other_check.id}") - assert %{"error" => "Access denied to this check"} = json_response(conn, 403) + assert %{"error" => "Check not found"} = json_response(conn, 404) end end diff --git a/test/towerops_web/controllers/api/v1/devices_controller_test.exs b/test/towerops_web/controllers/api/v1/devices_controller_test.exs index 569126ce..84333f49 100644 --- a/test/towerops_web/controllers/api/v1/devices_controller_test.exs +++ b/test/towerops_web/controllers/api/v1/devices_controller_test.exs @@ -182,7 +182,7 @@ defmodule ToweropsWeb.Api.V1.DevicesControllerTest do assert org_id == organization.id end - test "returns 403 when site belongs to different organization", %{conn: conn, organization: organization} do + test "returns 404 when site belongs to different organization", %{conn: conn, organization: organization} do # Enable sites for the authenticated organization {:ok, _organization} = Towerops.Organizations.update_organization(organization, %{use_sites: true}) @@ -204,10 +204,10 @@ defmodule ToweropsWeb.Api.V1.DevicesControllerTest do conn = post(conn, ~p"/api/v1/devices", device_params) - assert %{"error" => "Access denied to this site"} = json_response(conn, 403) + assert %{"error" => "Site not found"} = json_response(conn, 404) end - test "returns 403 when site_id does not exist", %{conn: conn, organization: organization} do + test "returns 404 when site_id does not exist", %{conn: conn, organization: organization} do # Enable sites for this organization {:ok, _organization} = Towerops.Organizations.update_organization(organization, %{use_sites: true}) @@ -221,7 +221,7 @@ defmodule ToweropsWeb.Api.V1.DevicesControllerTest do conn = post(conn, ~p"/api/v1/devices", device_params) - assert %{"error" => "Site not found"} = json_response(conn, 403) + assert %{"error" => "Site not found"} = json_response(conn, 404) end test "returns 422 with invalid device params", %{conn: conn} do @@ -277,7 +277,7 @@ defmodule ToweropsWeb.Api.V1.DevicesControllerTest do assert %{"error" => "Device not found"} = json_response(conn, 404) end - test "returns 403 when device belongs to different organization", %{conn: conn} do + test "returns 404 when device belongs to different organization", %{conn: conn} do # Create another organization with a device other_user = user_fixture() other_org = organization_fixture(other_user.id) @@ -285,7 +285,7 @@ defmodule ToweropsWeb.Api.V1.DevicesControllerTest do conn = get(conn, ~p"/api/v1/devices/#{other_device.id}") - assert %{"error" => "Access denied to this device"} = json_response(conn, 403) + assert %{"error" => "Device not found"} = json_response(conn, 404) end end @@ -323,7 +323,7 @@ defmodule ToweropsWeb.Api.V1.DevicesControllerTest do assert %{"error" => "Device not found"} = json_response(conn, 404) end - test "returns 403 when device belongs to different organization", %{conn: conn} do + test "returns 404 when device belongs to different organization", %{conn: conn} do # Create another organization with a device other_user = user_fixture() other_org = organization_fixture(other_user.id) @@ -337,7 +337,7 @@ defmodule ToweropsWeb.Api.V1.DevicesControllerTest do conn = patch(conn, ~p"/api/v1/devices/#{other_device.id}", update_params) - assert %{"error" => "Access denied to this device"} = json_response(conn, 403) + assert %{"error" => "Device not found"} = json_response(conn, 404) end test "returns 422 with invalid update params", %{conn: conn, organization: organization} do @@ -388,7 +388,7 @@ defmodule ToweropsWeb.Api.V1.DevicesControllerTest do assert %{"error" => "Device not found"} = json_response(conn, 404) end - test "returns 403 when device belongs to different organization", %{conn: conn} do + test "returns 404 when device belongs to different organization", %{conn: conn} do # Create another organization with a device other_user = user_fixture() other_org = organization_fixture(other_user.id) @@ -396,7 +396,7 @@ defmodule ToweropsWeb.Api.V1.DevicesControllerTest do conn = delete(conn, ~p"/api/v1/devices/#{other_device.id}") - assert %{"error" => "Access denied to this device"} = json_response(conn, 403) + assert %{"error" => "Device not found"} = json_response(conn, 404) end end end diff --git a/test/towerops_web/controllers/api/v1/escalation_policies_controller_test.exs b/test/towerops_web/controllers/api/v1/escalation_policies_controller_test.exs index 254bd2cf..e3e2a584 100644 --- a/test/towerops_web/controllers/api/v1/escalation_policies_controller_test.exs +++ b/test/towerops_web/controllers/api/v1/escalation_policies_controller_test.exs @@ -132,14 +132,14 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesControllerTest do assert %{"error" => "Escalation policy not found"} = json_response(conn, 404) end - test "returns 403 when policy belongs to different organization", %{conn: conn} do + test "returns 404 when policy belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_policy = escalation_policy_fixture(other_org.id) conn = get(conn, ~p"/api/v1/escalation_policies/#{other_policy.id}") - assert %{"error" => "Access denied to this escalation policy"} = json_response(conn, 403) + assert %{"error" => "Escalation policy not found"} = json_response(conn, 404) end end @@ -160,7 +160,7 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesControllerTest do assert %{"error" => "Escalation policy not found"} = json_response(conn, 404) end - test "returns 403 when policy belongs to different organization", %{conn: conn} do + test "returns 404 when policy belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_policy = escalation_policy_fixture(other_org.id) @@ -168,7 +168,7 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesControllerTest do params = %{"escalation_policy" => %{"name" => "Hacked"}} conn = patch(conn, ~p"/api/v1/escalation_policies/#{other_policy.id}", params) - assert %{"error" => "Access denied to this escalation policy"} = json_response(conn, 403) + assert %{"error" => "Escalation policy not found"} = json_response(conn, 404) end test "returns 400 when escalation_policy parameter is missing", %{ @@ -199,14 +199,14 @@ defmodule ToweropsWeb.Api.V1.EscalationPoliciesControllerTest do assert %{"error" => "Escalation policy not found"} = json_response(conn, 404) end - test "returns 403 when policy belongs to different organization", %{conn: conn} do + test "returns 404 when policy belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_policy = escalation_policy_fixture(other_org.id) conn = delete(conn, ~p"/api/v1/escalation_policies/#{other_policy.id}") - assert %{"error" => "Access denied to this escalation policy"} = json_response(conn, 403) + assert %{"error" => "Escalation policy not found"} = json_response(conn, 404) end end diff --git a/test/towerops_web/controllers/api/v1/maintenance_windows_controller_test.exs b/test/towerops_web/controllers/api/v1/maintenance_windows_controller_test.exs index 5669666f..53b0cc4d 100644 --- a/test/towerops_web/controllers/api/v1/maintenance_windows_controller_test.exs +++ b/test/towerops_web/controllers/api/v1/maintenance_windows_controller_test.exs @@ -164,7 +164,7 @@ defmodule ToweropsWeb.Api.V1.MaintenanceWindowsControllerTest do assert %{"error" => "Maintenance window not found"} = json_response(conn, 404) end - test "returns 403 when window belongs to different organization", %{conn: conn} do + test "returns 404 when window belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) @@ -176,7 +176,7 @@ defmodule ToweropsWeb.Api.V1.MaintenanceWindowsControllerTest do conn = get(conn, ~p"/api/v1/maintenance_windows/#{other_window.id}") - assert %{"error" => "Access denied to this maintenance window"} = json_response(conn, 403) + assert %{"error" => "Maintenance window not found"} = json_response(conn, 404) end end @@ -242,7 +242,7 @@ defmodule ToweropsWeb.Api.V1.MaintenanceWindowsControllerTest do assert %{"error" => "Maintenance window not found"} = json_response(conn, 404) end - test "returns 403 when window belongs to different organization", %{conn: conn} do + test "returns 404 when window belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) @@ -254,7 +254,7 @@ defmodule ToweropsWeb.Api.V1.MaintenanceWindowsControllerTest do conn = delete(conn, ~p"/api/v1/maintenance_windows/#{other_window.id}") - assert %{"error" => "Access denied to this maintenance window"} = json_response(conn, 403) + assert %{"error" => "Maintenance window not found"} = json_response(conn, 404) end end diff --git a/test/towerops_web/controllers/api/v1/schedules_controller_test.exs b/test/towerops_web/controllers/api/v1/schedules_controller_test.exs index f9a994b9..efff3406 100644 --- a/test/towerops_web/controllers/api/v1/schedules_controller_test.exs +++ b/test/towerops_web/controllers/api/v1/schedules_controller_test.exs @@ -137,14 +137,14 @@ defmodule ToweropsWeb.Api.V1.SchedulesControllerTest do assert %{"error" => "Schedule not found"} = json_response(conn, 404) end - test "returns 403 when schedule belongs to different organization", %{conn: conn} do + test "returns 404 when schedule belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_schedule = schedule_fixture(other_org.id) conn = get(conn, ~p"/api/v1/schedules/#{other_schedule.id}") - assert %{"error" => "Access denied to this schedule"} = json_response(conn, 403) + assert %{"error" => "Schedule not found"} = json_response(conn, 404) end end @@ -165,7 +165,7 @@ defmodule ToweropsWeb.Api.V1.SchedulesControllerTest do assert %{"error" => "Schedule not found"} = json_response(conn, 404) end - test "returns 403 when schedule belongs to different organization", %{conn: conn} do + test "returns 404 when schedule belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_schedule = schedule_fixture(other_org.id) @@ -173,7 +173,7 @@ defmodule ToweropsWeb.Api.V1.SchedulesControllerTest do params = %{"schedule" => %{"name" => "Hacked"}} conn = patch(conn, ~p"/api/v1/schedules/#{other_schedule.id}", params) - assert %{"error" => "Access denied to this schedule"} = json_response(conn, 403) + assert %{"error" => "Schedule not found"} = json_response(conn, 404) end test "returns 400 when schedule parameter is missing", %{ @@ -204,14 +204,14 @@ defmodule ToweropsWeb.Api.V1.SchedulesControllerTest do assert %{"error" => "Schedule not found"} = json_response(conn, 404) end - test "returns 403 when schedule belongs to different organization", %{conn: conn} do + test "returns 404 when schedule belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) other_schedule = schedule_fixture(other_org.id) conn = delete(conn, ~p"/api/v1/schedules/#{other_schedule.id}") - assert %{"error" => "Access denied to this schedule"} = json_response(conn, 403) + assert %{"error" => "Schedule not found"} = json_response(conn, 404) end end diff --git a/test/towerops_web/controllers/api/v1/sites_controller_test.exs b/test/towerops_web/controllers/api/v1/sites_controller_test.exs index 3a468f4a..0f83a101 100644 --- a/test/towerops_web/controllers/api/v1/sites_controller_test.exs +++ b/test/towerops_web/controllers/api/v1/sites_controller_test.exs @@ -173,7 +173,7 @@ defmodule ToweropsWeb.Api.V1.SitesControllerTest do assert %{"error" => "Site not found"} = json_response(conn, 404) end - test "returns 403 when site belongs to different organization", %{conn: conn} do + test "returns 404 when site belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) @@ -182,7 +182,7 @@ defmodule ToweropsWeb.Api.V1.SitesControllerTest do conn = get(conn, ~p"/api/v1/sites/#{other_site.id}") - assert %{"error" => "Access denied to this site"} = json_response(conn, 403) + assert %{"error" => "Site not found"} = json_response(conn, 404) end end @@ -225,7 +225,7 @@ defmodule ToweropsWeb.Api.V1.SitesControllerTest do assert %{"error" => "Site not found"} = json_response(conn, 404) end - test "returns 403 when site belongs to different organization", %{conn: conn} do + test "returns 404 when site belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) @@ -240,7 +240,7 @@ defmodule ToweropsWeb.Api.V1.SitesControllerTest do conn = patch(conn, ~p"/api/v1/sites/#{other_site.id}", update_params) - assert %{"error" => "Access denied to this site"} = json_response(conn, 403) + assert %{"error" => "Site not found"} = json_response(conn, 404) end test "returns 422 with invalid update params", %{conn: conn, organization: organization} do @@ -293,7 +293,7 @@ defmodule ToweropsWeb.Api.V1.SitesControllerTest do assert %{"error" => "Site not found"} = json_response(conn, 404) end - test "returns 403 when site belongs to different organization", %{conn: conn} do + test "returns 404 when site belongs to different organization", %{conn: conn} do other_user = user_fixture() other_org = organization_fixture(other_user.id) @@ -302,7 +302,7 @@ defmodule ToweropsWeb.Api.V1.SitesControllerTest do conn = delete(conn, ~p"/api/v1/sites/#{other_site.id}") - assert %{"error" => "Access denied to this site"} = json_response(conn, 403) + assert %{"error" => "Site not found"} = json_response(conn, 404) end end