diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex index 0e677fa567..806fcd0f37 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/address_controller.ex @@ -12,6 +12,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do import BlockScoutWeb.PagingHelper, only: [delete_parameters_from_next_page_params: 1, token_transfers_types_options: 1] + alias BlockScoutWeb.AccessHelpers alias BlockScoutWeb.API.V2.{AddressView, BlockView, TransactionView} alias Explorer.{Chain, Market} alias Indexer.Fetcher.TokenBalanceOnDemand @@ -38,8 +39,9 @@ defmodule BlockScoutWeb.API.V2.AddressController do action_fallback(BlockScoutWeb.API.V2.FallbackController) - def address(conn, %{"address_hash" => address_hash_string}) do + def address(conn, %{"address_hash" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params), {:not_found, {:ok, address}} <- {:not_found, Chain.hash_to_address(address_hash)} do conn |> put_status(200) @@ -47,8 +49,9 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def counters(conn, %{"address_hash" => address_hash_string}) do + def counters(conn, %{"address_hash" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params), {:not_found, {:ok, address}} <- {:not_found, Chain.hash_to_address(address_hash)} do {validation_count} = Chain.address_counters(address) @@ -65,8 +68,9 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def token_balances(conn, %{"address_hash" => address_hash_string}) do - with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + def token_balances(conn, %{"address_hash" => address_hash_string} = params) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do token_balances = address_hash |> Chain.fetch_last_token_balances() @@ -86,7 +90,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do end def transactions(conn, %{"address_hash" => address_hash_string} = params) do - with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do options = @transaction_necessity_by_association |> Keyword.merge(paging_options(params)) @@ -106,7 +111,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do end def token_transfers(conn, %{"address_hash" => address_hash_string} = params) do - with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do options = @token_transfer_necessity_by_association |> Keyword.merge(paging_options(params)) @@ -132,7 +138,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do end def internal_transactions(conn, %{"address_hash" => address_hash_string} = params) do - with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do full_options = [ necessity_by_association: %{ @@ -164,7 +171,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do end def logs(conn, %{"address_hash" => address_hash_string, "topic" => topic} = params) do - with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do prepared_topic = String.trim(topic) formatted_topic = if String.starts_with?(prepared_topic, "0x"), do: prepared_topic, else: "0x" <> prepared_topic @@ -183,7 +191,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do end def logs(conn, %{"address_hash" => address_hash_string} = params) do - with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do results_plus_one = Chain.address_to_logs(address_hash, paging_options(params)) {logs, next_page} = split_list_by_page(results_plus_one) @@ -197,7 +206,8 @@ defmodule BlockScoutWeb.API.V2.AddressController do end def blocks_validated(conn, %{"address_hash" => address_hash_string} = params) do - with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do full_options = Keyword.merge( [ @@ -225,6 +235,7 @@ defmodule BlockScoutWeb.API.V2.AddressController do def coin_balance_history(conn, %{"address_hash" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params), {:not_found, {:ok, _address}, _} <- {:not_found, Chain.hash_to_address(address_hash), :empty_items_with_next_page_params} do full_options = paging_options(params) @@ -243,8 +254,9 @@ defmodule BlockScoutWeb.API.V2.AddressController do end end - def coin_balance_history_by_day(conn, %{"address_hash" => address_hash_string}) do - with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + def coin_balance_history_by_day(conn, %{"address_hash" => address_hash_string} = params) do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do balances_by_day = address_hash |> Chain.address_to_balances_by_day(true) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex index b61ef38777..d808d6aa0e 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex @@ -40,4 +40,11 @@ defmodule BlockScoutWeb.API.V2.FallbackController do conn |> call({:not_found, nil}) end + + def call(conn, {:restricted_access, true}) do + conn + |> put_status(:forbidden) + |> put_view(ApiView) + |> render(:message, %{message: "Restricted access"}) + end end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex index 79998a4aaa..b13b265a7e 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/token_controller.ex @@ -1,6 +1,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do use BlockScoutWeb, :controller + alias BlockScoutWeb.AccessHelpers alias BlockScoutWeb.API.V2.TransactionView alias Explorer.Chain @@ -9,8 +10,9 @@ defmodule BlockScoutWeb.API.V2.TokenController do action_fallback(BlockScoutWeb.API.V2.FallbackController) - def token(conn, %{"address_hash" => address_hash_string}) do + def token(conn, %{"address_hash" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params), {:not_found, {:ok, token}} <- {:not_found, Chain.token_from_address_hash(address_hash)} do conn |> put_status(200) @@ -18,8 +20,9 @@ defmodule BlockScoutWeb.API.V2.TokenController do end end - def counters(conn, %{"address_hash" => address_hash_string}) do + def counters(conn, %{"address_hash" => address_hash_string} = params) do with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params), {:not_found, {:ok, _}} <- {:not_found, Chain.token_from_address_hash(address_hash)} do {transfer_count, token_holder_count} = Chain.fetch_token_counters(address_hash, 30_000) @@ -28,7 +31,8 @@ defmodule BlockScoutWeb.API.V2.TokenController do end def transfers(conn, %{"address_hash" => address_hash_string} = params) do - with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)} do + with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params) do results_plus_one = Chain.fetch_token_transfers_from_token_hash(address_hash, paging_options(params)) {token_transfers, next_page} = split_list_by_page(results_plus_one) @@ -47,6 +51,7 @@ defmodule BlockScoutWeb.API.V2.TokenController do from_api = true with {:format, {:ok, address_hash}} <- {:format, Chain.string_to_address_hash(address_hash_string)}, + {:ok, false} <- AccessHelpers.restricted_access?(address_hash_string, params), {:not_found, {:ok, token}, _} <- {:not_found, Chain.token_from_address_hash(address_hash), :empty_items_with_next_page_params} do results_plus_one = Chain.fetch_token_holders_from_token_hash(address_hash, from_api, paging_options(params)) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index 4b6a6c9599..b66c5726cf 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -13,6 +13,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do type_filter_options: 1 ] + alias BlockScoutWeb.AccessHelpers alias Explorer.Chain alias Explorer.Chain.Import alias Explorer.Chain.Import.Runner.InternalTransactions @@ -59,7 +60,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do } ] - def transaction(conn, %{"transaction_hash" => transaction_hash_string}) do + def transaction(conn, %{"transaction_hash" => transaction_hash_string} = params) do with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, {:not_found, {:ok, transaction}} <- {:not_found, @@ -67,6 +68,8 @@ defmodule BlockScoutWeb.API.V2.TransactionController do transaction_hash, necessity_by_association: @transaction_necessity_by_association )}, + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params), preloaded <- Chain.preload_token_transfers(transaction, @token_transfers_in_tx_neccessity_by_association, false) do conn @@ -97,10 +100,12 @@ defmodule BlockScoutWeb.API.V2.TransactionController do |> render(:transactions, %{transactions: transactions, next_page_params: next_page_params}) end - def raw_trace(conn, %{"transaction_hash" => transaction_hash_string}) do + def raw_trace(conn, %{"transaction_hash" => transaction_hash_string} = params) do with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, {:not_found, {:ok, transaction}} <- - {:not_found, Chain.hash_to_transaction(transaction_hash)} do + {:not_found, Chain.hash_to_transaction(transaction_hash)}, + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do if is_nil(transaction.block_number) do conn |> put_status(200) @@ -158,7 +163,11 @@ defmodule BlockScoutWeb.API.V2.TransactionController do end def token_transfers(conn, %{"transaction_hash" => transaction_hash_string} = params) do - with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)} do + with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, + {:not_found, {:ok, transaction}} <- + {:not_found, Chain.hash_to_transaction(transaction_hash)}, + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do full_options = [necessity_by_association: @token_transfers_neccessity_by_association] |> Keyword.merge(paging_options(params)) @@ -180,7 +189,11 @@ defmodule BlockScoutWeb.API.V2.TransactionController do end def internal_transactions(conn, %{"transaction_hash" => transaction_hash_string} = params) do - with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)} do + with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, + {:not_found, {:ok, transaction}} <- + {:not_found, Chain.hash_to_transaction(transaction_hash)}, + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do full_options = Keyword.merge( @internal_transaction_neccessity_by_association, @@ -206,7 +219,11 @@ defmodule BlockScoutWeb.API.V2.TransactionController do end def logs(conn, %{"transaction_hash" => transaction_hash_string} = params) do - with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)} do + with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)}, + {:not_found, {:ok, transaction}} <- + {:not_found, Chain.hash_to_transaction(transaction_hash)}, + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.from_address_hash), params), + {:ok, false} <- AccessHelpers.restricted_access?(to_string(transaction.to_address_hash), params) do full_options = Keyword.merge( [ diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs index 036b79dfc9..b7693129ff 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/v2/transaction_controller_test.exs @@ -117,13 +117,11 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end describe "/transactions/{tx_hash}/internal-transactions" do - test "return empty list on non existing tx", %{conn: conn} do + test "return 404 on non existing tx", %{conn: conn} do tx = build(:transaction) request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/internal-transactions") - assert response = json_response(request, 200) - assert response["items"] == [] - assert response["next_page_params"] == nil + assert %{"message" => "Not found"} = json_response(request, 404) end test "return 422 on invalid tx hash", %{conn: conn} do @@ -236,13 +234,11 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end describe "/transactions/{tx_hash}/logs" do - test "return empty list on non existing tx", %{conn: conn} do + test "return 404 on non existing tx", %{conn: conn} do tx = build(:transaction) request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/logs") - assert response = json_response(request, 200) - assert response["items"] == [] - assert response["next_page_params"] == nil + assert %{"message" => "Not found"} = json_response(request, 404) end test "return 422 on invalid tx hash", %{conn: conn} do @@ -330,13 +326,11 @@ defmodule BlockScoutWeb.API.V2.TransactionControllerTest do end describe "/transactions/{tx_hash}/token-transfers" do - test "return empty list on non existing tx", %{conn: conn} do + test "return 404 on non existing tx", %{conn: conn} do tx = build(:transaction) request = get(conn, "/api/v2/transactions/#{to_string(tx.hash)}/token-transfers") - assert response = json_response(request, 200) - assert response["items"] == [] - assert response["next_page_params"] == nil + assert %{"message" => "Not found"} = json_response(request, 404) end test "return 422 on invalid tx hash", %{conn: conn} do