fix(portal): Fix update API endpoint for resources (#7493)

Why:

* The API endpoint for updating Resources was using
`Resources.fetch_resource_by_id_or_persistent_id`, however that function
was fetching all Resources, which included deleted Resources. In order
to prevent an API user from attempting to update a Resource that is
deleted, a new function was added to fetch active Resources only.

Fixes: #7492
This commit is contained in:
Brian Manifold
2024-12-12 14:51:28 -08:00
committed by GitHub
parent 67161afd2c
commit 9711cf56c1
3 changed files with 47 additions and 2 deletions

View File

@@ -92,7 +92,7 @@ defmodule API.ResourceController do
subject = conn.assigns.subject
attrs = set_param_defaults(params)
with {:ok, resource} <- Resources.fetch_resource_by_id_or_persistent_id(id, subject) do
with {:ok, resource} <- Resources.fetch_active_resource_by_id_or_persistent_id(id, subject) do
case Resources.update_or_replace_resource(resource, attrs, subject) do
{:updated, updated_resource} ->
render(conn, :show, resource: updated_resource)

View File

@@ -5,10 +5,14 @@ defmodule API.ResourceControllerTest do
setup do
account = Fixtures.Accounts.create_account()
actor = Fixtures.Actors.create_actor(type: :api_client, account: account)
identity = Fixtures.Auth.create_identity(account: account, actor: actor)
subject = Fixtures.Auth.create_subject(identity: identity)
%{
account: account,
actor: actor
actor: actor,
identity: identity,
subject: subject
}
end
@@ -195,6 +199,27 @@ defmodule API.ResourceControllerTest do
assert resp == %{"error" => %{"reason" => "Bad Request"}}
end
test "returns not found when resource is deleted", %{
conn: conn,
account: account,
actor: actor,
subject: subject
} do
resource = Fixtures.Resources.create_resource(%{account: account})
Domain.Resources.delete_resource(resource, subject)
attrs = %{"name" => "Google"}
conn =
conn
|> authorize_conn(actor)
|> put_req_header("content-type", "application/json")
|> put("/resources/#{resource.id}", resource: attrs)
assert resp = json_response(conn, 404)
assert resp == %{"error" => %{"reason" => "Not Found"}}
end
test "updates a resource", %{conn: conn, account: account, actor: actor} do
resource = Fixtures.Resources.create_resource(%{account: account})

View File

@@ -43,6 +43,26 @@ defmodule Domain.Resources do
end
end
def fetch_active_resource_by_id_or_persistent_id(id, %Auth.Subject{} = subject, opts \\ []) do
required_permissions =
{:one_of,
[
Authorizer.manage_resources_permission(),
Authorizer.view_available_resources_permission()
]}
with :ok <- Auth.ensure_has_permissions(subject, required_permissions),
true <- Repo.valid_uuid?(id) do
Resource.Query.not_deleted()
|> Resource.Query.by_id_or_persistent_id(id)
|> Authorizer.for_subject(Resource, subject)
|> Repo.fetch(Resource.Query, opts)
else
false -> {:error, :not_found}
other -> other
end
end
def fetch_and_authorize_resource_by_id(id, %Auth.Subject{} = subject, opts \\ []) do
with :ok <-
Auth.ensure_has_permissions(subject, Authorizer.view_available_resources_permission()),