forked from AkkomaGang/akkoma
account visibility
This commit is contained in:
parent
b0ccdb5af4
commit
e7bc2f980c
6 changed files with 105 additions and 47 deletions
|
@ -262,37 +262,51 @@ def account_status(%User{deactivated: true}), do: :deactivated
|
||||||
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)
|
boolean()
|
||||||
|
| :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: true
|
||||||
|
|
||||||
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)
|
superuser?(for_user) || visible_account_status(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def visible_for?(_, _), do: false
|
def visible_for(_, _), do: false
|
||||||
|
|
||||||
|
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)
|
||||||
|
status in [:active, :password_reset_pending] || status
|
||||||
|
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
|
||||||
|
|
|
@ -102,7 +102,9 @@ def show_operation 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),
|
||||||
404 => Operation.response("Error", "application/json", ApiError)
|
401 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
410 => Operation.response("Error", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -142,7 +144,9 @@ def statuses_operation 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()),
|
||||||
404 => Operation.response("Error", "application/json", ApiError)
|
401 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
410 => Operation.response("Error", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -221,17 +221,17 @@ def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, [])
|
||||||
@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
|
true <- 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
|
true <- User.visible_for(user, reading_user) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.delete(:tagged)
|
|> Map.delete(:tagged)
|
||||||
|
@ -250,7 +250,20 @@ def statuses(%{assigns: %{user: reading_user}} = conn, params) 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
|
||||||
|
:deactivated ->
|
||||||
|
render_error(conn, :gone, "")
|
||||||
|
|
||||||
|
: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
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ def render("index.json", %{users: users} = opts) 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]) == true do
|
||||||
do_render("show.json", opts)
|
do_render("show.json", opts)
|
||||||
else
|
else
|
||||||
%{}
|
%{}
|
||||||
|
|
|
@ -1289,11 +1289,11 @@ test "returns false for a non-invisible user" 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)
|
||||||
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
|
||||||
|
@ -1302,14 +1302,14 @@ test "returns false when the account is unauthenticated and auth is 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)
|
||||||
|
|
||||||
refute User.visible_for?(user, other_user)
|
refute User.visible_for(user, other_user) == true
|
||||||
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)
|
||||||
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
|
||||||
|
@ -1318,7 +1318,7 @@ test "returns true when the account is unauthenticated and being viewed by a pri
|
||||||
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)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,15 @@ test "returns 404 for internal.fetch actor", %{conn: conn} 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 401 for deactivated user", %{conn: conn} do
|
||||||
|
user = insert(:user, deactivated: true)
|
||||||
|
|
||||||
|
assert %{} =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/accounts/#{user.id}")
|
||||||
|
|> json_response_and_validate_schema(:gone)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp local_and_remote_users do
|
defp local_and_remote_users do
|
||||||
|
@ -143,15 +152,15 @@ defp local_and_remote_users 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 @@ test "if user is authenticated", %{local: local, remote: remote} 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 @@ test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} d
|
||||||
|
|
||||||
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 @@ test "works with announces that are just addressed to public", %{conn: conn} do
|
||||||
assert id == announce.id
|
assert id == announce.id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "deactivated user", %{conn: conn} do
|
||||||
|
user = insert(:user, deactivated: true)
|
||||||
|
|
||||||
|
assert %{} ==
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/accounts/#{user.id}/statuses")
|
||||||
|
|> json_response_and_validate_schema(:gone)
|
||||||
|
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)
|
||||||
|
@ -422,15 +449,15 @@ defp local_and_remote_activities(%{local: local, remote: remote}) 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
|
||||||
|
@ -451,10 +478,10 @@ test "if user is authenticated", %{local: local, remote: remote} 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
|
||||||
|
@ -481,10 +508,10 @@ test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} d
|
||||||
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
|
||||||
|
|
Loading…
Reference in a new issue