Merge branch 'feature/1739-account-endpoints' into 'develop'

account visibility in masto api

Closes #1739

See merge request pleroma/pleroma!2488
This commit is contained in:
lain 2020-06-22 12:37:10 +00:00
commit 59bdef0c33
6 changed files with 107 additions and 45 deletions

View File

@ -263,37 +263,60 @@ defmodule Pleroma.User do
def account_status(%User{password_reset_pending: true}), do: :password_reset_pending def account_status(%User{password_reset_pending: true}), do: :password_reset_pending
def account_status(%User{confirmation_pending: true}) do def account_status(%User{confirmation_pending: true}) do
case Config.get([:instance, :account_activation_required]) do if Config.get([:instance, :account_activation_required]) do
true -> :confirmation_pending :confirmation_pending
_ -> :active else
:active
end end
end end
def account_status(%User{}), do: :active def account_status(%User{}), do: :active
@spec visible_for?(User.t(), User.t() | nil) :: boolean() @spec visible_for(User.t(), User.t() | nil) ::
def visible_for?(user, for_user \\ nil) :visible
| :invisible
| :restricted_unauthenticated
| :deactivated
| :confirmation_pending
def visible_for(user, for_user \\ nil)
def visible_for?(%User{invisible: true}, _), do: false def visible_for(%User{invisible: true}, _), do: :invisible
def visible_for?(%User{id: user_id}, %User{id: user_id}), do: true def visible_for(%User{id: user_id}, %User{id: user_id}), do: :visible
def visible_for?(%User{local: local} = user, nil) do def visible_for(%User{} = user, nil) do
cfg_key = if restrict_unauthenticated?(user) do
if local, :restrict_unauthenticated
do: :local, else
else: :remote visible_account_status(user)
end
if Config.get([:restrict_unauthenticated, :profiles, cfg_key]),
do: false,
else: account_status(user) == :active
end end
def visible_for?(%User{} = user, for_user) do def visible_for(%User{} = user, for_user) do
account_status(user) == :active || superuser?(for_user) if superuser?(for_user) do
:visible
else
visible_account_status(user)
end
end end
def visible_for?(_, _), do: false def visible_for(_, _), do: :invisible
defp restrict_unauthenticated?(%User{local: local}) do
config_key = if local, do: :local, else: :remote
Config.get([:restrict_unauthenticated, :profiles, config_key], false)
end
defp visible_account_status(user) do
status = account_status(user)
if status in [:active, :password_reset_pending] do
:visible
else
status
end
end
@spec superuser?(User.t()) :: boolean() @spec superuser?(User.t()) :: boolean()
def superuser?(%User{local: true, is_admin: true}), do: true def superuser?(%User{local: true, is_admin: true}), do: true

View File

@ -102,6 +102,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
responses: %{ responses: %{
200 => Operation.response("Account", "application/json", Account), 200 => Operation.response("Account", "application/json", Account),
401 => Operation.response("Error", "application/json", ApiError),
404 => Operation.response("Error", "application/json", ApiError) 404 => Operation.response("Error", "application/json", ApiError)
} }
} }
@ -142,6 +143,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
] ++ pagination_params(), ] ++ pagination_params(),
responses: %{ responses: %{
200 => Operation.response("Statuses", "application/json", array_of_statuses()), 200 => Operation.response("Statuses", "application/json", array_of_statuses()),
401 => Operation.response("Error", "application/json", ApiError),
404 => Operation.response("Error", "application/json", ApiError) 404 => Operation.response("Error", "application/json", ApiError)
} }
} }

View File

@ -234,17 +234,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
@doc "GET /api/v1/accounts/:id" @doc "GET /api/v1/accounts/:id"
def show(%{assigns: %{user: for_user}} = conn, %{id: nickname_or_id}) do def show(%{assigns: %{user: for_user}} = conn, %{id: nickname_or_id}) do
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user), with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user),
true <- User.visible_for?(user, for_user) do :visible <- User.visible_for(user, for_user) do
render(conn, "show.json", user: user, for: for_user) render(conn, "show.json", user: user, for: for_user)
else else
_e -> render_error(conn, :not_found, "Can't find user") error -> user_visibility_error(conn, error)
end end
end end
@doc "GET /api/v1/accounts/:id/statuses" @doc "GET /api/v1/accounts/:id/statuses"
def statuses(%{assigns: %{user: reading_user}} = conn, params) do def statuses(%{assigns: %{user: reading_user}} = conn, params) do
with %User{} = user <- User.get_cached_by_nickname_or_id(params.id, for: reading_user), with %User{} = user <- User.get_cached_by_nickname_or_id(params.id, for: reading_user),
true <- User.visible_for?(user, reading_user) do :visible <- User.visible_for(user, reading_user) do
params = params =
params params
|> Map.delete(:tagged) |> Map.delete(:tagged)
@ -261,7 +261,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
as: :activity as: :activity
) )
else else
_e -> render_error(conn, :not_found, "Can't find user") error -> user_visibility_error(conn, error)
end
end
defp user_visibility_error(conn, error) do
case error do
:restrict_unauthenticated ->
render_error(conn, :unauthorized, "This API requires an authenticated user")
_ ->
render_error(conn, :not_found, "Can't find user")
end end
end end

View File

@ -35,7 +35,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
end end
def render("show.json", %{user: user} = opts) do def render("show.json", %{user: user} = opts) do
if User.visible_for?(user, opts[:for]) do if User.visible_for(user, opts[:for]) == :visible do
do_render("show.json", opts) do_render("show.json", opts)
else else
%{} %{}

View File

@ -1342,11 +1342,11 @@ defmodule Pleroma.UserTest do
end end
end end
describe "visible_for?/2" do describe "visible_for/2" do
test "returns true when the account is itself" do test "returns true when the account is itself" do
user = insert(:user, local: true) user = insert(:user, local: true)
assert User.visible_for?(user, user) assert User.visible_for(user, user) == :visible
end end
test "returns false when the account is unauthenticated and auth is required" do test "returns false when the account is unauthenticated and auth is required" do
@ -1355,14 +1355,14 @@ defmodule Pleroma.UserTest do
user = insert(:user, local: true, confirmation_pending: true) user = insert(:user, local: true, confirmation_pending: true)
other_user = insert(:user, local: true) other_user = insert(:user, local: true)
refute User.visible_for?(user, other_user) refute User.visible_for(user, other_user) == :visible
end end
test "returns true when the account is unauthenticated and auth is not required" do test "returns true when the account is unauthenticated and auth is not required" do
user = insert(:user, local: true, confirmation_pending: true) user = insert(:user, local: true, confirmation_pending: true)
other_user = insert(:user, local: true) other_user = insert(:user, local: true)
assert User.visible_for?(user, other_user) assert User.visible_for(user, other_user) == :visible
end end
test "returns true when the account is unauthenticated and being viewed by a privileged account (auth required)" do test "returns true when the account is unauthenticated and being viewed by a privileged account (auth required)" do
@ -1371,7 +1371,7 @@ defmodule Pleroma.UserTest do
user = insert(:user, local: true, confirmation_pending: true) user = insert(:user, local: true, confirmation_pending: true)
other_user = insert(:user, local: true, is_admin: true) other_user = insert(:user, local: true, is_admin: true)
assert User.visible_for?(user, other_user) assert User.visible_for(user, other_user) == :visible
end end
end end

View File

@ -127,6 +127,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|> get("/api/v1/accounts/internal.fetch") |> get("/api/v1/accounts/internal.fetch")
|> json_response_and_validate_schema(404) |> json_response_and_validate_schema(404)
end end
test "returns 404 for deactivated user", %{conn: conn} do
user = insert(:user, deactivated: true)
assert %{"error" => "Can't find user"} =
conn
|> get("/api/v1/accounts/#{user.id}")
|> json_response_and_validate_schema(:not_found)
end
end end
defp local_and_remote_users do defp local_and_remote_users do
@ -143,15 +152,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true) setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
assert %{"error" => "Can't find user"} == assert %{"error" => "This API requires an authenticated user"} ==
conn conn
|> get("/api/v1/accounts/#{local.id}") |> get("/api/v1/accounts/#{local.id}")
|> json_response_and_validate_schema(:not_found) |> json_response_and_validate_schema(:unauthorized)
assert %{"error" => "Can't find user"} == assert %{"error" => "This API requires an authenticated user"} ==
conn conn
|> get("/api/v1/accounts/#{remote.id}") |> get("/api/v1/accounts/#{remote.id}")
|> json_response_and_validate_schema(:not_found) |> json_response_and_validate_schema(:unauthorized)
end end
test "if user is authenticated", %{local: local, remote: remote} do test "if user is authenticated", %{local: local, remote: remote} do
@ -173,8 +182,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
res_conn = get(conn, "/api/v1/accounts/#{local.id}") res_conn = get(conn, "/api/v1/accounts/#{local.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{ assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
"error" => "Can't find user" "error" => "This API requires an authenticated user"
} }
res_conn = get(conn, "/api/v1/accounts/#{remote.id}") res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
@ -203,8 +212,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
res_conn = get(conn, "/api/v1/accounts/#{remote.id}") res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{ assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
"error" => "Can't find user" "error" => "This API requires an authenticated user"
} }
end end
@ -249,6 +258,24 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
assert id == announce.id assert id == announce.id
end end
test "deactivated user", %{conn: conn} do
user = insert(:user, deactivated: true)
assert %{"error" => "Can't find user"} ==
conn
|> get("/api/v1/accounts/#{user.id}/statuses")
|> json_response_and_validate_schema(:not_found)
end
test "returns 404 when user is invisible", %{conn: conn} do
user = insert(:user, %{invisible: true})
assert %{"error" => "Can't find user"} =
conn
|> get("/api/v1/accounts/#{user.id}")
|> json_response_and_validate_schema(404)
end
test "respects blocks", %{user: user_one, conn: conn} do test "respects blocks", %{user: user_one, conn: conn} do
user_two = insert(:user) user_two = insert(:user)
user_three = insert(:user) user_three = insert(:user)
@ -430,15 +457,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true) setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
assert %{"error" => "Can't find user"} == assert %{"error" => "This API requires an authenticated user"} ==
conn conn
|> get("/api/v1/accounts/#{local.id}/statuses") |> get("/api/v1/accounts/#{local.id}/statuses")
|> json_response_and_validate_schema(:not_found) |> json_response_and_validate_schema(:unauthorized)
assert %{"error" => "Can't find user"} == assert %{"error" => "This API requires an authenticated user"} ==
conn conn
|> get("/api/v1/accounts/#{remote.id}/statuses") |> get("/api/v1/accounts/#{remote.id}/statuses")
|> json_response_and_validate_schema(:not_found) |> json_response_and_validate_schema(:unauthorized)
end end
test "if user is authenticated", %{local: local, remote: remote} do test "if user is authenticated", %{local: local, remote: remote} do
@ -459,10 +486,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true) setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
assert %{"error" => "Can't find user"} == assert %{"error" => "This API requires an authenticated user"} ==
conn conn
|> get("/api/v1/accounts/#{local.id}/statuses") |> get("/api/v1/accounts/#{local.id}/statuses")
|> json_response_and_validate_schema(:not_found) |> json_response_and_validate_schema(:unauthorized)
res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses") res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
assert length(json_response_and_validate_schema(res_conn, 200)) == 1 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
@ -489,10 +516,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses") res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
assert length(json_response_and_validate_schema(res_conn, 200)) == 1 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
assert %{"error" => "Can't find user"} == assert %{"error" => "This API requires an authenticated user"} ==
conn conn
|> get("/api/v1/accounts/#{remote.id}/statuses") |> get("/api/v1/accounts/#{remote.id}/statuses")
|> json_response_and_validate_schema(:not_found) |> json_response_and_validate_schema(:unauthorized)
end end
test "if user is authenticated", %{local: local, remote: remote} do test "if user is authenticated", %{local: local, remote: remote} do