From 6be0ab1e55e2e9f0c1e6f937631cfd3da899bc79 Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 2 Dec 2018 17:35:32 +0100 Subject: [PATCH 1/7] Hide network in ap. --- lib/pleroma/user/info.ex | 1 + .../web/activity_pub/views/user_view.ex | 12 ++++---- .../activity_pub_controller_test.exs | 28 +++++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 94d403bf7..83fab7e3d 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -24,6 +24,7 @@ defmodule Pleroma.User.Info do field(:topic, :string, default: nil) field(:hub, :string, default: nil) field(:salmon, :string, default: nil) + field(:hide_network, :boolean, default: false) # Found in the wild # ap_id -> Where is this used? diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index aaa777602..869934172 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -82,7 +82,7 @@ def render("following.json", %{user: user, page: page}) do query = from(user in query, select: [:ap_id]) following = Repo.all(query) - collection(following, "#{user.ap_id}/following", page) + collection(following, "#{user.ap_id}/following", page, !user.info.hide_network) |> Map.merge(Utils.make_json_ld_header()) end @@ -95,7 +95,7 @@ def render("following.json", %{user: user}) do "id" => "#{user.ap_id}/following", "type" => "OrderedCollection", "totalItems" => length(following), - "first" => collection(following, "#{user.ap_id}/following", 1) + "first" => collection(following, "#{user.ap_id}/following", 1, !user.info.hide_network) } |> Map.merge(Utils.make_json_ld_header()) end @@ -105,7 +105,7 @@ def render("followers.json", %{user: user, page: page}) do query = from(user in query, select: [:ap_id]) followers = Repo.all(query) - collection(followers, "#{user.ap_id}/followers", page) + collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_network) |> Map.merge(Utils.make_json_ld_header()) end @@ -118,7 +118,7 @@ def render("followers.json", %{user: user}) do "id" => "#{user.ap_id}/followers", "type" => "OrderedCollection", "totalItems" => length(followers), - "first" => collection(followers, "#{user.ap_id}/followers", 1) + "first" => collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_network) } |> Map.merge(Utils.make_json_ld_header()) end @@ -172,7 +172,7 @@ def render("outbox.json", %{user: user, max_id: max_qid}) do end end - def collection(collection, iri, page, total \\ nil) do + def collection(collection, iri, page, show_items \\ true, total \\ nil) do offset = (page - 1) * 10 items = Enum.slice(collection, offset, 10) items = Enum.map(items, fn user -> user.ap_id end) @@ -183,7 +183,7 @@ def collection(collection, iri, page, total \\ nil) do "type" => "OrderedCollectionPage", "partOf" => iri, "totalItems" => total, - "orderedItems" => items + "orderedItems" => if(show_items, do: items, else: []) } if offset < total do diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index 1c24b348c..d8c5badb8 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -145,6 +145,20 @@ test "it returns the followers in a collection", %{conn: conn} do assert result["first"]["orderedItems"] == [user.ap_id] end + test "it returns returns empty if the user has 'hide_network' set", %{conn: conn} do + user = insert(:user) + user_two = insert(:user, %{info: %{hide_network: true}}) + User.follow(user, user_two) + + result = + conn + |> get("/users/#{user_two.nickname}/followers") + |> json_response(200) + + assert result["first"]["orderedItems"] == [] + assert result["totalItems"] == 1 + end + test "it works for more than 10 users", %{conn: conn} do user = insert(:user) @@ -186,6 +200,20 @@ test "it returns the following in a collection", %{conn: conn} do assert result["first"]["orderedItems"] == [user_two.ap_id] end + test "it returns returns empty if the user has 'hide_network' set", %{conn: conn} do + user = insert(:user, %{info: %{hide_network: true}}) + user_two = insert(:user) + User.follow(user, user_two) + + result = + conn + |> get("/users/#{user.nickname}/following") + |> json_response(200) + + assert result["first"]["orderedItems"] == [] + assert result["totalItems"] == 1 + end + test "it works for more than 10 users", %{conn: conn} do user = insert(:user) From 2a639de9b30aacd44b8ba648b872ad6cb7ab1d6c Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 2 Dec 2018 17:48:00 +0100 Subject: [PATCH 2/7] MastodonApi: Implement hide_network. --- .../mastodon_api/mastodon_api_controller.ex | 3 ++- .../mastodon_api_controller_test.exs | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index d19d55044..715a2f1a9 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -502,10 +502,10 @@ def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) end - # TODO: Pagination def followers(conn, %{"id" => id}) do with %User{} = user <- Repo.get(User, id), {:ok, followers} <- User.get_followers(user) do + followers = if(user.info.hide_network, do: [], else: followers) render(conn, AccountView, "accounts.json", %{users: followers, as: :user}) end end @@ -513,6 +513,7 @@ def followers(conn, %{"id" => id}) do def following(conn, %{"id" => id}) do with %User{} = user <- Repo.get(User, id), {:ok, followers} <- User.get_friends(user) do + followers = if(user.info.hide_network, do: [], else: followers) render(conn, AccountView, "accounts.json", %{users: followers, as: :user}) end end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 7042a6ace..9333b709e 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -1002,6 +1002,18 @@ test "getting followers", %{conn: conn} do assert id == to_string(user.id) end + test "getting followers, hide_network", %{conn: conn} do + user = insert(:user) + other_user = insert(:user, %{info: %{hide_network: true}}) + {:ok, user} = User.follow(user, other_user) + + conn = + conn + |> get("/api/v1/accounts/#{other_user.id}/followers") + + assert [] == json_response(conn, 200) + end + test "getting following", %{conn: conn} do user = insert(:user) other_user = insert(:user) @@ -1015,6 +1027,18 @@ test "getting following", %{conn: conn} do assert id == to_string(other_user.id) end + test "getting following, hide_network", %{conn: conn} do + user = insert(:user, %{info: %{hide_network: true}}) + other_user = insert(:user) + {:ok, user} = User.follow(user, other_user) + + conn = + conn + |> get("/api/v1/accounts/#{user.id}/following") + + assert [] == json_response(conn, 200) + end + test "following / unfollowing a user", %{conn: conn} do user = insert(:user) other_user = insert(:user) From 8c9a4e8b403d4575400f239dd87dab2ae7ecec6c Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 2 Dec 2018 18:14:13 +0100 Subject: [PATCH 3/7] TwitterAPI: Implement hide_network. --- .../web/twitter_api/twitter_api_controller.ex | 2 + .../twitter_api_controller_test.exs | 59 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index ff644dd79..a59badfdb 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -343,6 +343,7 @@ def external_profile(%{assigns: %{user: current_user}} = conn, %{"profileurl" => def followers(conn, params) do with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params), {:ok, followers} <- User.get_followers(user) do + followers = if(user.info.hide_network, do: [], else: followers) render(conn, UserView, "index.json", %{users: followers, for: conn.assigns[:user]}) else _e -> bad_request_reply(conn, "Can't get followers") @@ -352,6 +353,7 @@ def followers(conn, params) do def friends(conn, params) do with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params), {:ok, friends} <- User.get_friends(user) do + friends = if(user.info.hide_network, do: [], else: friends) render(conn, UserView, "index.json", %{users: friends, for: conn.assigns[:user]}) else _e -> bad_request_reply(conn, "Can't get friends") diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 89c176da7..8655105f3 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -855,6 +855,48 @@ test "it returns a user's followers", %{conn: conn} do result = json_response(conn, 200) assert Enum.sort(expected) == Enum.sort(result) end + + test "it returns a given user's followers with user_id", %{conn: conn} do + user = insert(:user) + follower_one = insert(:user) + follower_two = insert(:user) + not_follower = insert(:user) + + {:ok, follower_one} = User.follow(follower_one, user) + {:ok, follower_two} = User.follow(follower_two, user) + + conn = + conn + |> assign(:user, not_follower) + |> get("/api/statuses/followers", %{"user_id" => user.id}) + + assert MapSet.equal?( + MapSet.new(json_response(conn, 200)), + MapSet.new( + UserView.render("index.json", %{ + users: [follower_one, follower_two], + for: not_follower + }) + ) + ) + end + + test "it returns empty for a hidden network", %{conn: conn} do + user = insert(:user, %{info: %{hide_network: true}}) + follower_one = insert(:user) + follower_two = insert(:user) + not_follower = insert(:user) + + {:ok, follower_one} = User.follow(follower_one, user) + {:ok, follower_two} = User.follow(follower_two, user) + + conn = + conn + |> assign(:user, not_follower) + |> get("/api/statuses/followers", %{"user_id" => user.id}) + + assert [] == json_response(conn, 200) + end end describe "GET /api/statuses/friends" do @@ -899,6 +941,23 @@ test "it returns a given user's friends with user_id", %{conn: conn} do ) end + test "it returns empty for a hidden network", %{conn: conn} do + user = insert(:user, %{info: %{hide_network: true}}) + followed_one = insert(:user) + followed_two = insert(:user) + not_followed = insert(:user) + + {:ok, user} = User.follow(user, followed_one) + {:ok, user} = User.follow(user, followed_two) + + conn = + conn + |> assign(:user, not_followed) + |> get("/api/statuses/friends", %{"user_id" => user.id}) + + assert [] == json_response(conn, 200) + end + test "it returns a given user's friends with screen_name", %{conn: conn} do user = insert(:user) followed_one = insert(:user) From 839526a9134ba85c0a45fe85740d04a54076224c Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 5 Dec 2018 19:22:40 +0100 Subject: [PATCH 4/7] TwitterAPI: Add network hiding. --- lib/pleroma/user/info.ex | 3 ++- .../web/twitter_api/twitter_api_controller.ex | 2 +- .../twitter_api_controller_test.exs | 24 +++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 83fab7e3d..cefe33577 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -135,7 +135,8 @@ def profile_update(info, params) do :locked, :no_rich_text, :default_scope, - :banner + :banner, + :hide_network ]) end diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index a59badfdb..959312683 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -431,7 +431,7 @@ def raw_empty_array(conn, _params) do defp build_info_cng(user, params) do info_params = - ["no_rich_text", "locked"] + ["no_rich_text", "locked", "hide_network"] |> Enum.reduce(%{}, fn key, res -> if value = params[key] do Map.put(res, key, value == "true") diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 8655105f3..6836019a5 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -1024,6 +1024,30 @@ test "it updates a user's profile", %{conn: conn} do assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user}) end + test "it sets and un-sets hide_network", %{conn: conn} do + user = insert(:user) + + conn + |> assign(:user, user) + |> post("/api/account/update_profile.json", %{ + "hide_network" => "true" + }) + + user = Repo.get!(User, user.id) + assert user.info.hide_network == true + + conn = + conn + |> assign(:user, user) + |> post("/api/account/update_profile.json", %{ + "hide_network" => "false" + }) + + user = Repo.get!(User, user.id) + assert user.info.hide_network == false + assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user}) + end + test "it locks an account", %{conn: conn} do user = insert(:user) From fdac215091332d5f7df818855b62e600870d6786 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 5 Dec 2018 21:14:06 +0100 Subject: [PATCH 5/7] TwitterAPI: Show users their own network. --- .../web/twitter_api/twitter_api_controller.ex | 22 ++++++++--- .../twitter_api_controller_test.exs | 38 +++++++++++++++++++ 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 959312683..591557cca 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -340,20 +340,32 @@ def external_profile(%{assigns: %{user: current_user}} = conn, %{"profileurl" => end end - def followers(conn, params) do - with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params), + def followers(%{assigns: %{user: for_user}} = conn, params) do + with {:ok, user} <- TwitterAPI.get_user(for_user, params), {:ok, followers} <- User.get_followers(user) do - followers = if(user.info.hide_network, do: [], else: followers) + followers = + cond do + for_user && user.id == for_user.id -> followers + user.info.hide_network -> [] + true -> followers + end + render(conn, UserView, "index.json", %{users: followers, for: conn.assigns[:user]}) else _e -> bad_request_reply(conn, "Can't get followers") end end - def friends(conn, params) do + def friends(%{assigns: %{user: for_user}} = conn, params) do with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params), {:ok, friends} <- User.get_friends(user) do - friends = if(user.info.hide_network, do: [], else: friends) + friends = + cond do + for_user && user.id == for_user.id -> friends + user.info.hide_network -> [] + true -> friends + end + render(conn, UserView, "index.json", %{users: friends, for: conn.assigns[:user]}) else _e -> bad_request_reply(conn, "Can't get friends") diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 6836019a5..61fcb23d8 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -897,6 +897,25 @@ test "it returns empty for a hidden network", %{conn: conn} do assert [] == json_response(conn, 200) end + + test "it returns the followers for a hidden network if requested by the user themselves", %{ + conn: conn + } do + user = insert(:user, %{info: %{hide_network: true}}) + follower_one = insert(:user) + follower_two = insert(:user) + not_follower = insert(:user) + + {:ok, follower_one} = User.follow(follower_one, user) + {:ok, follower_two} = User.follow(follower_two, user) + + conn = + conn + |> assign(:user, user) + |> get("/api/statuses/followers", %{"user_id" => user.id}) + + refute [] == json_response(conn, 200) + end end describe "GET /api/statuses/friends" do @@ -958,6 +977,25 @@ test "it returns empty for a hidden network", %{conn: conn} do assert [] == json_response(conn, 200) end + test "it returns friends for a hidden network if the user themselves request it", %{ + conn: conn + } do + user = insert(:user, %{info: %{hide_network: true}}) + followed_one = insert(:user) + followed_two = insert(:user) + not_followed = insert(:user) + + {:ok, user} = User.follow(user, followed_one) + {:ok, user} = User.follow(user, followed_two) + + conn = + conn + |> assign(:user, user) + |> get("/api/statuses/friends", %{"user_id" => user.id}) + + refute [] == json_response(conn, 200) + end + test "it returns a given user's friends with screen_name", %{conn: conn} do user = insert(:user) followed_one = insert(:user) From 3ea4476445a5e9b6ec1625d7caa537f79254e9d0 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 5 Dec 2018 21:25:06 +0100 Subject: [PATCH 6/7] MastodonAPI: Show users their own network. --- .../mastodon_api/mastodon_api_controller.ex | 20 +++++++++++--- .../mastodon_api_controller_test.exs | 26 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 715a2f1a9..c8b2eae4d 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -502,18 +502,30 @@ def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) end - def followers(conn, %{"id" => id}) do + def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do with %User{} = user <- Repo.get(User, id), {:ok, followers} <- User.get_followers(user) do - followers = if(user.info.hide_network, do: [], else: followers) + followers = + cond do + for_user && user.id == for_user.id -> followers + user.info.hide_network -> [] + true -> followers + end + render(conn, AccountView, "accounts.json", %{users: followers, as: :user}) end end - def following(conn, %{"id" => id}) do + def following(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do with %User{} = user <- Repo.get(User, id), {:ok, followers} <- User.get_friends(user) do - followers = if(user.info.hide_network, do: [], else: followers) + followers = + cond do + for_user && user.id == for_user.id -> followers + user.info.hide_network -> [] + true -> followers + end + render(conn, AccountView, "accounts.json", %{users: followers, as: :user}) end end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 9333b709e..0b20daff3 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -1014,6 +1014,19 @@ test "getting followers, hide_network", %{conn: conn} do assert [] == json_response(conn, 200) end + test "getting followers, hide_network, same user requesting", %{conn: conn} do + user = insert(:user) + other_user = insert(:user, %{info: %{hide_network: true}}) + {:ok, user} = User.follow(user, other_user) + + conn = + conn + |> assign(:user, other_user) + |> get("/api/v1/accounts/#{other_user.id}/followers") + + refute [] == json_response(conn, 200) + end + test "getting following", %{conn: conn} do user = insert(:user) other_user = insert(:user) @@ -1039,6 +1052,19 @@ test "getting following, hide_network", %{conn: conn} do assert [] == json_response(conn, 200) end + test "getting following, hide_network, same user requesting", %{conn: conn} do + user = insert(:user, %{info: %{hide_network: true}}) + other_user = insert(:user) + {:ok, user} = User.follow(user, other_user) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/accounts/#{user.id}/following") + + refute [] == json_response(conn, 200) + end + test "following / unfollowing a user", %{conn: conn} do user = insert(:user) other_user = insert(:user) From 7d5720f2e40015502fe646b86768ab8454f571fe Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 5 Dec 2018 21:31:02 +0100 Subject: [PATCH 7/7] Fix merge. --- lib/pleroma/user/info.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 6829c26f6..7a99787f8 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -136,7 +136,7 @@ def profile_update(info, params) do :no_rich_text, :default_scope, :banner, - :hide_network + :hide_network, :background ]) end