From c2d592c9c5db8e0392948d5fc589761f482fc4f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20K=C3=BChl?= Date: Tue, 18 Sep 2018 11:39:06 +0200 Subject: [PATCH 01/44] Assign token to connection --- lib/pleroma/plugs/oauth_plug.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/plugs/oauth_plug.ex b/lib/pleroma/plugs/oauth_plug.ex index 0380ce14d..651485e09 100644 --- a/lib/pleroma/plugs/oauth_plug.ex +++ b/lib/pleroma/plugs/oauth_plug.ex @@ -18,10 +18,11 @@ def call(conn, _) do end with token when not is_nil(token) <- token, - %Token{user_id: user_id} <- Repo.get_by(Token, token: token), + %Token{user_id: user_id} = token <- Repo.get_by(Token, token: token), %User{} = user <- Repo.get(User, user_id), false <- !!user.info["deactivated"] do conn + |> assign(:token, token) |> assign(:user, user) else _ -> conn From d94ee5cd50005a947c3c3a734b086fd5cd266c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20K=C3=BChl?= Date: Tue, 18 Sep 2018 11:56:46 +0200 Subject: [PATCH 02/44] Mastodon API: Support push subscription CRUD --- .../mastodon_api/mastodon_api_controller.ex | 27 ++++++++ .../views/push_subscription_view.ex | 14 ++++ lib/pleroma/web/push/subscription.ex | 66 +++++++++++++++++++ lib/pleroma/web/router.ex | 5 ++ ...180918182427_create_push_subscriptions.exs | 18 +++++ 5 files changed, 130 insertions(+) create mode 100644 lib/pleroma/web/mastodon_api/views/push_subscription_view.ex create mode 100644 lib/pleroma/web/push/subscription.ex create mode 100644 priv/repo/migrations/20180918182427_create_push_subscriptions.exs diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 391a79885..7f06ee607 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1138,6 +1138,33 @@ def delete_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do json(conn, %{}) end + def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do + Pleroma.Web.Push.Subscription.delete_if_exists(user, token) + {:ok, subscription} = Pleroma.Web.Push.Subscription.create(user, token, params) + view = PushSubscriptionView.render("push_subscription.json", subscription: subscription) + json(conn, view) + end + + def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do + subscription = Pleroma.Web.Push.Subscription.get(user, token) + view = PushSubscriptionView.render("push_subscription.json", subscription: subscription) + json(conn, view) + end + + def update_push_subscription( + %{assigns: %{user: user, token: token}} = conn, + params + ) do + {:ok, subscription} = Pleroma.Web.Push.Subscription.update(user, token, params) + view = PushSubscriptionView.render("push_subscription.json", subscription: subscription) + json(conn, view) + end + + def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do + {:ok, _response} = Pleroma.Web.Push.Subscription.delete(user, token) + json(conn, %{}) + end + def errors(conn, _) do conn |> put_status(500) diff --git a/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex b/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex new file mode 100644 index 000000000..a910bb43e --- /dev/null +++ b/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex @@ -0,0 +1,14 @@ +defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do + use Pleroma.Web, :view + alias Pleroma.Web.MastodonAPI.PushSubscriptionView + + def render("push_subscription.json", %{subscription: subscription}) do + %{ + id: to_string(subscription.id), + endpoint: subscription.endpoint, + alerts: Map.get(subscription.data, "alerts"), + # TODO: generate VAPID server key + server_key: "N/A" + } + end +end diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex new file mode 100644 index 000000000..dc8fe9f33 --- /dev/null +++ b/lib/pleroma/web/push/subscription.ex @@ -0,0 +1,66 @@ +defmodule Pleroma.Web.Push.Subscription do + use Ecto.Schema + import Ecto.{Changeset, Query} + alias Pleroma.{Repo, User} + alias Pleroma.Web.OAuth.Token + alias Pleroma.Web.Push.Subscription + + schema "push_subscriptions" do + belongs_to(:user, User) + belongs_to(:token, Token) + field(:endpoint, :string) + field(:key_p256dh, :string) + field(:key_auth, :string) + field(:data, :map, default: %{}) + + timestamps() + end + + @supported_alert_types ~w[follow favourite mention reblog] + + defp alerts(%{"data" => %{"alerts" => alerts}}) do + alerts = Map.take(alerts, @supported_alert_types) + %{"alerts" => alerts} + end + + def create( + %User{} = user, + %Token{} = token, + %{ + "subscription" => %{ + "endpoint" => endpoint, + "keys" => %{"auth" => key_auth, "p256dh" => key_p256dh} + } + } = params + ) do + Repo.insert(%Subscription{ + user_id: user.id, + token_id: token.id, + endpoint: endpoint, + key_auth: key_auth, + key_p256dh: key_p256dh, + data: alerts(params) + }) + end + + def get(%User{id: user_id}, %Token{id: token_id}) do + Repo.get_by(Subscription, user_id: user_id, token_id: token_id) + end + + def update(user, token, params) do + get(user, token) + |> change(data: alerts(params)) + |> Repo.update() + end + + def delete(user, token) do + Repo.delete(get(user, token)) + end + + def delete_if_exists(user, token) do + case get(user, token) do + nil -> {:ok, nil} + sub -> Repo.delete(sub) + end + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index ddfaa8c42..04dc80444 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -171,6 +171,11 @@ defmodule Pleroma.Web.Router do put("/filters/:id", MastodonAPIController, :update_filter) delete("/filters/:id", MastodonAPIController, :delete_filter) + post("/push/subscription", MastodonAPIController, :create_push_subscription) + get("/push/subscription", MastodonAPIController, :get_push_subscription) + put("/push/subscription", MastodonAPIController, :update_push_subscription) + delete("/push/subscription", MastodonAPIController, :delete_push_subscription) + get("/suggestions", MastodonAPIController, :suggestions) get("/endorsements", MastodonAPIController, :empty_array) diff --git a/priv/repo/migrations/20180918182427_create_push_subscriptions.exs b/priv/repo/migrations/20180918182427_create_push_subscriptions.exs new file mode 100644 index 000000000..0cc7afa54 --- /dev/null +++ b/priv/repo/migrations/20180918182427_create_push_subscriptions.exs @@ -0,0 +1,18 @@ +defmodule Pleroma.Repo.Migrations.CreatePushSubscriptions do + use Ecto.Migration + + def change do + create table("push_subscriptions") do + add :user_id, references("users", on_delete: :delete_all) + add :token_id, references("oauth_tokens", on_delete: :delete_all) + add :endpoint, :string + add :key_p256dh, :string + add :key_auth, :string + add :data, :map + + timestamps() + end + + create index("push_subscriptions", [:user_id, :token_id], unique: true) + end +end From 6be0ab1e55e2e9f0c1e6f937631cfd3da899bc79 Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 2 Dec 2018 17:35:32 +0100 Subject: [PATCH 03/44] 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 04/44] 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 05/44] 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 826fc446d56b48b67e97144c74bbf74109fb8168 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 4 Dec 2018 18:35:57 +0300 Subject: [PATCH 06/44] [#210] TwitterAPI: implemented /api/media/metadata/create to allow uploads description (alt text) setting. --- lib/pleroma/web/router.ex | 1 + .../web/twitter_api/twitter_api_controller.ex | 18 ++++++++++++++++- .../twitter_api_controller_test.exs | 20 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index d6a9d5779..b7c79d2eb 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -324,6 +324,7 @@ defmodule Pleroma.Web.Router do post("/statusnet/media/upload", TwitterAPI.Controller, :upload) post("/media/upload", TwitterAPI.Controller, :upload_json) + post("/media/metadata/create", TwitterAPI.Controller, :update_media) post("/favorites/create/:id", TwitterAPI.Controller, :favorite) post("/favorites/create", TwitterAPI.Controller, :favorite) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 961250d92..a9e45f91e 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -4,7 +4,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView} alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils - alias Pleroma.{Repo, Activity, User, Notification} + alias Pleroma.{Repo, Activity, Object, User, Notification} alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Utils alias Ecto.Changeset @@ -226,6 +226,22 @@ def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do end end + @doc "https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create" + def update_media(%{assigns: %{user: _}} = conn, %{"media_id" => id} = data) do + description = get_in(data, ["alt_text", "text"]) || data["name"] || data["description"] + + with %Object{} = object <- Repo.get(Object, id), is_binary(description) do + new_data = Map.put(object.data, "name", description) + + change = Object.change(object, %{data: new_data}) + {:ok, _} = Repo.update(change) + end + + conn + |> put_status(:no_content) + |> json("") + end + def upload(conn, %{"media" => media}) do response = TwitterAPI.upload(media) diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index a6495ffc1..8faa4b58e 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -1253,4 +1253,24 @@ test "it returns users, ordered by similarity", %{conn: conn} do assert [user.id, user_two.id, user_three.id] == Enum.map(resp, fn %{"id" => id} -> id end) end end + + describe "POST /api/media/metadata/create" do + test "it updates `data[name]` of referenced Object with provided value", %{conn: conn} do + user = insert(:user) + object = insert(:note) + description = "Informative description of the image. Initial: #{object.data["name"]}}" + + _conn = + conn + |> assign(:user, user) + |> post("/api/media/metadata/create.json", %{ + "media_id" => object.id, + "alt_text" => %{"text" => description} + }) + |> json_response(:no_content) + + object = Repo.get!(Object, object.id) + assert object.data["name"] == description + end + end end From 48e6193bf2c6a03068f1c6a96429fadffaa7794b Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 4 Dec 2018 19:24:41 +0300 Subject: [PATCH 07/44] [#210] Refactoring. --- lib/pleroma/web/twitter_api/twitter_api_controller.ex | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index a9e45f91e..c846dbd60 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -232,9 +232,7 @@ def update_media(%{assigns: %{user: _}} = conn, %{"media_id" => id} = data) do with %Object{} = object <- Repo.get(Object, id), is_binary(description) do new_data = Map.put(object.data, "name", description) - - change = Object.change(object, %{data: new_data}) - {:ok, _} = Repo.update(change) + {:ok, _} = object |> Object.change(%{data: new_data}) |> Repo.update() end conn From 44ab3dbe2c3d25a1772b99679653eaf96d4fbd8b Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 4 Dec 2018 19:45:09 +0300 Subject: [PATCH 08/44] [#210] Refactoring. --- .../twitter_api/twitter_api_controller_test.exs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 8faa4b58e..21e844124 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -1260,14 +1260,13 @@ test "it updates `data[name]` of referenced Object with provided value", %{conn: object = insert(:note) description = "Informative description of the image. Initial: #{object.data["name"]}}" - _conn = - conn - |> assign(:user, user) - |> post("/api/media/metadata/create.json", %{ - "media_id" => object.id, - "alt_text" => %{"text" => description} - }) - |> json_response(:no_content) + conn + |> assign(:user, user) + |> post("/api/media/metadata/create.json", %{ + "media_id" => object.id, + "alt_text" => %{"text" => description} + }) + |> json_response(:no_content) object = Repo.get!(Object, object.id) assert object.data["name"] == description From 8a1df182cf836eafc2558be32cf58ad07839f46a Mon Sep 17 00:00:00 2001 From: scarlett Date: Tue, 4 Dec 2018 21:39:18 +0000 Subject: [PATCH 09/44] Add a MRF Policy for appending re: to identical subjects in replies. --- config/config.md | 1 + .../activity_pub/mrf/ensure_re_prepended.ex | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex diff --git a/config/config.md b/config/config.md index 47e838dd6..d90d18566 100644 --- a/config/config.md +++ b/config/config.md @@ -39,6 +39,7 @@ Note: `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`. * `Pleroma.Web.ActivityPub.MRF.DropPolicy`: Drops all activities. It generally doesn’t makes sense to use in production * `Pleroma.Web.ActivityPub.MRF.SimplePolicy`: Restrict the visibility of activities from certains instances (See ``:mrf_simple`` section) * `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See ``:mrf_rejectnonpublic`` section) + * `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:. * `public`: Makes the client API in authentificated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. * `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send. * `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json`` diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex new file mode 100644 index 000000000..3f216010e --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex @@ -0,0 +1,37 @@ +defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do + alias Pleroma.Activity + + @behaviour Pleroma.Web.ActivityPub.MRF + + def filter_by_summary( + %{"summary" => parent_summary} = parent, + %{"summary" => child_summary} = child + ) + when not is_nil(child_summary) and child_summary == parent_summary and + byte_size(child_summary) > 1 do + if not String.starts_with?(child_summary, "re:") do + Map.put(child, "summary", "re: " <> child_summary) + else + child + end + end + + def filter_by_summary(parent, child), do: child + + def filter(%{"type" => activity_type} = object) when activity_type == "Create" do + child = object["object"] + in_reply_to = Activity.get_create_activity_by_object_ap_id(child["inReplyTo"]) + + child = + if(in_reply_to, + do: filter_by_summary(in_reply_to.data["object"], child), + else: child + ) + + object = Map.put(object, "object", child) + + {:ok, object} + end + + def filter(object), do: {:ok, object} +end From a418547bdfc95b72497faa0eb5da69b04f6a8566 Mon Sep 17 00:00:00 2001 From: hakabahitoyo Date: Wed, 5 Dec 2018 16:08:34 +0900 Subject: [PATCH 10/44] debug /api/v1/suggestions --- .../web/mastodon_api/mastodon_api_controller.ex | 10 +++++++++- 1 file changed, 9 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 ea64f163d..f9007a808 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1169,7 +1169,15 @@ def suggestions(%{assigns: %{user: user}} = conn, _) do url = String.replace(api, "{{host}}", host) |> String.replace("{{user}}", user) with {:ok, %{status: 200, body: body}} <- - @httpoison.get(url, [], timeout: timeout, recv_timeout: timeout), + @httpoison.get( + url, + [], + follow_redirect: true, + adapter: [ + timeout: timeout, + recv_timeout: timeout + ] + ), {:ok, data} <- Jason.decode(body) do data2 = Enum.slice(data, 0, limit) From be187f82f798a904755fc754538d560cde901a88 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 5 Dec 2018 11:48:50 +0300 Subject: [PATCH 11/44] [#210] Further refactoring. --- .../web/twitter_api/twitter_api_controller.ex | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index c846dbd60..c9e845aea 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -226,13 +226,21 @@ def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do end end - @doc "https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create" + @doc """ + Updates metadata of uploaded media object. + Derived from [Twitter API endpoint](https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create). + """ def update_media(%{assigns: %{user: _}} = conn, %{"media_id" => id} = data) do description = get_in(data, ["alt_text", "text"]) || data["name"] || data["description"] - with %Object{} = object <- Repo.get(Object, id), is_binary(description) do + with %Object{} = object <- Repo.get(Object, id), + is_binary(description) do new_data = Map.put(object.data, "name", description) - {:ok, _} = object |> Object.change(%{data: new_data}) |> Repo.update() + + {:ok, _} = + object + |> Object.change(%{data: new_data}) + |> Repo.update() end conn From 53797d19c5e8463388862eaa20931c8cb78d66a6 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 5 Dec 2018 11:56:31 +0300 Subject: [PATCH 12/44] [#210] Test update (replaced bang- with non-bang method). --- test/web/twitter_api/twitter_api_controller_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 21e844124..478763de7 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -1268,7 +1268,7 @@ test "it updates `data[name]` of referenced Object with provided value", %{conn: }) |> json_response(:no_content) - object = Repo.get!(Object, object.id) + object = Repo.get(Object, object.id) assert object.data["name"] == description end end From 848151f7cbf372d008c178d13c9a74942164c955 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 5 Dec 2018 13:37:06 +0300 Subject: [PATCH 13/44] [#210] [TwitterAPI] Made actor be stored for uploads. Added ownership check to `update_media` action. Added controller tests for `upload` and `update_media` actions. Refactoring. --- lib/pleroma/web/activity_pub/activity_pub.ex | 3 +- lib/pleroma/web/twitter_api/twitter_api.ex | 8 ++- .../web/twitter_api/twitter_api_controller.ex | 43 +++++++----- test/support/data_case.ex | 17 +++++ test/upload_test.exs | 17 +---- .../mastodon_api_controller_test.exs | 2 +- .../twitter_api_controller_test.exs | 67 +++++++++++++++++-- test/web/twitter_api/twitter_api_test.exs | 3 +- 8 files changed, 120 insertions(+), 40 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 7e207c620..39692163f 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -574,7 +574,8 @@ def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do def upload(file, opts \\ []) do with {:ok, data} <- Upload.store(file, opts) do - Repo.insert(%Object{data: data}) + obj_data = if opts[:actor], do: Map.put(data, "actor", opts[:actor]), else: data + Repo.insert(%Object{data: obj_data}) end end diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index c19a4f084..b9468ab03 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -93,8 +93,12 @@ def unfav(%User{} = user, ap_id_or_id) do end end - def upload(%Plug.Upload{} = file, format \\ "xml") do - {:ok, object} = ActivityPub.upload(file) + def ap_upload(%Plug.Upload{} = file, %User{} = user) do + ActivityPub.upload(file, actor: User.ap_id(user)) + end + + def upload(%Plug.Upload{} = file, %User{} = user, format \\ "xml") do + {:ok, object} = ap_upload(file, user) url = List.first(object.data["url"]) href = url["href"] diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index c9e845aea..2f12131e7 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -230,34 +230,47 @@ def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do Updates metadata of uploaded media object. Derived from [Twitter API endpoint](https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create). """ - def update_media(%{assigns: %{user: _}} = conn, %{"media_id" => id} = data) do + def update_media(%{assigns: %{user: user}} = conn, %{"media_id" => id} = data) do + object = Repo.get(Object, id) description = get_in(data, ["alt_text", "text"]) || data["name"] || data["description"] - with %Object{} = object <- Repo.get(Object, id), - is_binary(description) do - new_data = Map.put(object.data, "name", description) + {conn, status, response_body} = + cond do + !object -> + {halt(conn), :not_found, ""} - {:ok, _} = - object - |> Object.change(%{data: new_data}) - |> Repo.update() - end + object.data["actor"] != User.ap_id(user) -> + {halt(conn), :forbidden, "You can only update your own uploads."} + + !is_binary(description) -> + {conn, :not_modified, ""} + + true -> + new_data = Map.put(object.data, "name", description) + + {:ok, _} = + object + |> Object.change(%{data: new_data}) + |> Repo.update() + + {conn, :no_content, ""} + end conn - |> put_status(:no_content) - |> json("") + |> put_status(status) + |> json(response_body) end - def upload(conn, %{"media" => media}) do - response = TwitterAPI.upload(media) + def upload(%{assigns: %{user: user}} = conn, %{"media" => media}) do + response = TwitterAPI.upload(media, user) conn |> put_resp_content_type("application/atom+xml") |> send_resp(200, response) end - def upload_json(conn, %{"media" => media}) do - response = TwitterAPI.upload(media, "json") + def upload_json(%{assigns: %{user: user}} = conn, %{"media" => media}) do + response = TwitterAPI.upload(media, user, "json") conn |> json_reply(200, response) diff --git a/test/support/data_case.ex b/test/support/data_case.ex index 8eff0fd94..9dde6b5e5 100644 --- a/test/support/data_case.ex +++ b/test/support/data_case.ex @@ -36,6 +36,23 @@ defmodule Pleroma.DataCase do :ok end + def ensure_local_uploader(_context) do + uploader = Pleroma.Config.get([Pleroma.Upload, :uploader]) + filters = Pleroma.Config.get([Pleroma.Upload, :filters]) + + unless uploader == Pleroma.Uploaders.Local || filters != [] do + Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + Pleroma.Config.put([Pleroma.Upload, :filters], []) + + on_exit(fn -> + Pleroma.Config.put([Pleroma.Upload, :uploader], uploader) + Pleroma.Config.put([Pleroma.Upload, :filters], filters) + end) + end + + :ok + end + @doc """ A helper that transform changeset errors to a map of messages. diff --git a/test/upload_test.exs b/test/upload_test.exs index b2ce755d2..f2cad4cf0 100644 --- a/test/upload_test.exs +++ b/test/upload_test.exs @@ -3,22 +3,7 @@ defmodule Pleroma.UploadTest do use Pleroma.DataCase describe "Storing a file with the Local uploader" do - setup do - uploader = Pleroma.Config.get([Pleroma.Upload, :uploader]) - filters = Pleroma.Config.get([Pleroma.Upload, :filters]) - - unless uploader == Pleroma.Uploaders.Local || filters != [] do - Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) - Pleroma.Config.put([Pleroma.Upload, :filters], []) - - on_exit(fn -> - Pleroma.Config.put([Pleroma.Upload, :uploader], uploader) - Pleroma.Config.put([Pleroma.Upload, :filters], filters) - end) - end - - :ok - end + setup [:ensure_local_uploader] test "returns a media url" do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 098acb59f..b5839cff1 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -804,7 +804,7 @@ test "gets an users media", %{conn: conn} do } media = - TwitterAPI.upload(file, "json") + TwitterAPI.upload(file, user, "json") |> Poison.decode!() {:ok, image_post} = diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 478763de7..c07dc6912 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -1254,15 +1254,74 @@ test "it returns users, ordered by similarity", %{conn: conn} do end end - describe "POST /api/media/metadata/create" do - test "it updates `data[name]` of referenced Object with provided value", %{conn: conn} do + describe "POST /api/media/upload" do + setup context do + Pleroma.DataCase.ensure_local_uploader(context) + end + + test "it performs the upload and sets `data[actor]` with AP id of uploader user", %{ + conn: conn + } do user = insert(:user) + + upload_filename = "test/fixtures/image_tmp.jpg" + File.cp!("test/fixtures/image.jpg", upload_filename) + + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname(upload_filename), + filename: "image.jpg" + } + + response = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/octet-stream") + |> post("/api/media/upload", %{ + "media" => file + }) + |> json_response(:ok) + + assert response["media_id"] + object = Repo.get(Object, response["media_id"]) + assert object + assert object.data["actor"] == User.ap_id(user) + end + end + + describe "POST /api/media/metadata/create" do + setup do object = insert(:note) - description = "Informative description of the image. Initial: #{object.data["name"]}}" + user = User.get_by_ap_id(object.data["actor"]) + %{object: object, user: user} + end + + test "it returns :forbidden status on attempt to modify someone else's upload", %{ + conn: conn, + object: object + } do + initial_description = object.data["name"] + another_user = insert(:user) + + conn + |> assign(:user, another_user) + |> post("/api/media/metadata/create", %{"media_id" => object.id}) + |> json_response(:forbidden) + + object = Repo.get(Object, object.id) + assert object.data["name"] == initial_description + end + + test "it updates `data[name]` of referenced Object with provided value", %{ + conn: conn, + object: object, + user: user + } do + description = "Informative description of the image. Initial value: #{object.data["name"]}}" conn |> assign(:user, user) - |> post("/api/media/metadata/create.json", %{ + |> post("/api/media/metadata/create", %{ "media_id" => object.id, "alt_text" => %{"text" => description} }) diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index 28230699f..e34fbbabd 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -182,13 +182,14 @@ test "Unblock another user using screen_name" do end test "upload a file" do + user = insert(:user) file = %Plug.Upload{ content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } - response = TwitterAPI.upload(file) + response = TwitterAPI.upload(file, user) assert is_binary(response) end From ba345e4c293449871f443669b73c262403237620 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 5 Dec 2018 13:43:00 +0300 Subject: [PATCH 14/44] [#210] Formatting fix. --- test/web/twitter_api/twitter_api_test.exs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index e34fbbabd..032701705 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -183,6 +183,7 @@ test "Unblock another user using screen_name" do test "upload a file" do user = insert(:user) + file = %Plug.Upload{ content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), From c524c50509d47fe04f2dc48b30ef1c3d6f6d2ffd Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Wed, 5 Dec 2018 17:29:49 +0300 Subject: [PATCH 15/44] fix/273 --- lib/pleroma/plugs/oauth_plug.ex | 74 +++++++++++++++++++++++++-------- test/plugs/oauth_plug_test.exs | 56 +++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 test/plugs/oauth_plug_test.exs diff --git a/lib/pleroma/plugs/oauth_plug.ex b/lib/pleroma/plugs/oauth_plug.ex index 630f15eec..75f9209c2 100644 --- a/lib/pleroma/plugs/oauth_plug.ex +++ b/lib/pleroma/plugs/oauth_plug.ex @@ -1,30 +1,68 @@ defmodule Pleroma.Plugs.OAuthPlug do import Plug.Conn - alias Pleroma.User - alias Pleroma.Repo - alias Pleroma.Web.OAuth.Token + import Ecto.Query - def init(options) do - options - end + alias Pleroma.{ + User, + Repo, + Web.OAuth.Token + } + + @realm_reg Regex.compile!("Bearer\:?\s+(.*)$", "i") + + def init(options), do: options def call(%{assigns: %{user: %User{}}} = conn, _), do: conn def call(conn, _) do - token = - case get_req_header(conn, "authorization") do - ["Bearer " <> header] -> header - _ -> get_session(conn, :oauth_token) - end - - with token when not is_nil(token) <- token, - %Token{user_id: user_id} <- Repo.get_by(Token, token: token), - %User{} = user <- Repo.get(User, user_id), - false <- !!user.info.deactivated do - conn - |> assign(:user, user) + with {:ok, token} <- fetch_token(conn), + {:ok, user} <- fetch_user(token) do + assign(conn, :user, user) else _ -> conn end end + + # Gets user by token + # + @spec fetch_user(String.t()) :: {:ok, User.t()} | nil + defp fetch_user(token) do + query = from(q in Token, where: q.token == ^token, preload: [:user]) + + with %Token{user: %{info: %{deactivated: false} = _} = user} <- Repo.one(query) do + {:ok, user} + end + end + + # Gets token from session by :oauth_token key + # + @spec fetch_token_from_session(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()} + defp fetch_token_from_session(conn) do + case get_session(conn, :oauth_token) do + nil -> :no_token_found + token -> {:ok, token} + end + end + + # Gets token from headers + # + @spec fetch_token(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()} + defp fetch_token(%Plug.Conn{} = conn) do + headers = get_req_header(conn, "authorization") + + with :no_token_found <- fetch_token(headers), + do: fetch_token_from_session(conn) + end + + @spec fetch_token(Keyword.t()) :: :no_token_found | {:ok, String.t()} + defp fetch_token([]), do: :no_token_found + + defp fetch_token([token | tail]) do + trimmed_token = String.trim(token) + + case Regex.run(@realm_reg, trimmed_token) do + [_, match] -> {:ok, String.trim(match)} + _ -> fetch_token(tail) + end + end end diff --git a/test/plugs/oauth_plug_test.exs b/test/plugs/oauth_plug_test.exs new file mode 100644 index 000000000..4dd12f207 --- /dev/null +++ b/test/plugs/oauth_plug_test.exs @@ -0,0 +1,56 @@ +defmodule Pleroma.Plugs.OAuthPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Plugs.OAuthPlug + import Pleroma.Factory + + @session_opts [ + store: :cookie, + key: "_test", + signing_salt: "cooldude" + ] + + setup %{conn: conn} do + user = insert(:user) + {:ok, %{token: token}} = Pleroma.Web.OAuth.Token.create_token(insert(:oauth_app), user) + %{user: user, token: token, conn: conn} + end + + test "with valid token(uppercase), it assigns the user", %{conn: conn} = opts do + conn = + conn + |> put_req_header("authorization", "BEARER #{opts[:token]}") + |> OAuthPlug.call(%{}) + + assert conn.assigns[:user] == opts[:user] + end + + test "with valid token(downcase), it assigns the user", %{conn: conn} = opts do + conn = + conn + |> put_req_header("authorization", "bearer #{opts[:token]}") + |> OAuthPlug.call(%{}) + + assert conn.assigns[:user] == opts[:user] + end + + test "with invalid token, it not assigns the user", %{conn: conn} do + conn = + conn + |> put_req_header("authorization", "bearer TTTTT") + |> OAuthPlug.call(%{}) + + refute conn.assigns[:user] + end + + test "when token is missed but token in session, it assigns the user", %{conn: conn} = opts do + conn = + conn + |> Plug.Session.call(Plug.Session.init(@session_opts)) + |> fetch_session() + |> put_session(:oauth_token, opts[:token]) + |> OAuthPlug.call(%{}) + + assert conn.assigns[:user] == opts[:user] + end +end From 5427d2af3a1bd9a5b571375b4aca2ccd9042b3b9 Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Wed, 5 Dec 2018 16:41:50 +0100 Subject: [PATCH 16/44] Update mix tasks since User.info.info_changeset is deprecated --- lib/mix/tasks/make_moderator.ex | 14 ++++++-------- lib/mix/tasks/set_admin.ex | 17 ++++++++--------- lib/mix/tasks/set_locked.ex | 18 ++++++++---------- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/lib/mix/tasks/make_moderator.ex b/lib/mix/tasks/make_moderator.ex index 15586dc30..9ea6c45c1 100644 --- a/lib/mix/tasks/make_moderator.ex +++ b/lib/mix/tasks/make_moderator.ex @@ -8,7 +8,7 @@ defmodule Mix.Tasks.SetModerator do """ use Mix.Task - import Mix.Ecto + import Ecto.Changeset alias Pleroma.{Repo, User} def run([nickname | rest]) do @@ -21,14 +21,12 @@ def run([nickname | rest]) do end with %User{local: true} = user <- User.get_by_nickname(nickname) do - info = - user.info - |> Map.put("is_moderator", !!moderator) + info_cng = User.Info.admin_api_update(user.info, %{is_moderator: !!moderator}) + user_cng = Ecto.Changeset.change(user) + |> put_embed(:info, info_cng) + {:ok, user} = User.update_and_set_cache(user_cng) - cng = User.info_changeset(user, %{info: info}) - {:ok, user} = User.update_and_set_cache(cng) - - IO.puts("Moderator status of #{nickname}: #{user.info["is_moderator"]}") + IO.puts("Moderator status of #{nickname}: #{user.info.is_moderator}") else _ -> IO.puts("No local user #{nickname}") diff --git a/lib/mix/tasks/set_admin.ex b/lib/mix/tasks/set_admin.ex index d5ccf261b..9788e49b9 100644 --- a/lib/mix/tasks/set_admin.ex +++ b/lib/mix/tasks/set_admin.ex @@ -1,5 +1,6 @@ defmodule Mix.Tasks.SetAdmin do use Mix.Task + import Ecto.Changeset alias Pleroma.User @doc """ @@ -9,21 +10,19 @@ defmodule Mix.Tasks.SetAdmin do def run([nickname | rest]) do Application.ensure_all_started(:pleroma) - status = + admin = case rest do - [status] -> status == "true" + [admin] -> admin == "true" _ -> true end with %User{local: true} = user <- User.get_by_nickname(nickname) do - info = - user.info - |> Map.put("is_admin", !!status) + info_cng = User.Info.admin_api_update(user.info, %{is_admin: !!admin}) + user_cng = Ecto.Changeset.change(user) + |> put_embed(:info, info_cng) + {:ok, user} = User.update_and_set_cache(user_cng) - cng = User.info_changeset(user, %{info: info}) - {:ok, user} = User.update_and_set_cache(cng) - - IO.puts("Admin status of #{nickname}: #{user.info["is_admin"]}") + IO.puts("Admin status of #{nickname}: #{user.info.is_admin}") else _ -> IO.puts("No local user #{nickname}") diff --git a/lib/mix/tasks/set_locked.ex b/lib/mix/tasks/set_locked.ex index a154595ca..42d978599 100644 --- a/lib/mix/tasks/set_locked.ex +++ b/lib/mix/tasks/set_locked.ex @@ -8,13 +8,13 @@ defmodule Mix.Tasks.SetLocked do Example: ``mix set_locked lain`` """ - + use Mix.Task - import Mix.Ecto + import Ecto.Changeset alias Pleroma.{Repo, User} def run([nickname | rest]) do - ensure_started(Repo, []) + Application.ensure_all_started(:pleroma) locked = case rest do @@ -23,14 +23,12 @@ def run([nickname | rest]) do end with %User{local: true} = user <- User.get_by_nickname(nickname) do - info = - user.info - |> Map.put("locked", !!locked) + info_cng = User.Info.profile_update(user.info, %{locked: !!locked}) + user_cng = Ecto.Changeset.change(user) + |> put_embed(:info, info_cng) + {:ok, user} = User.update_and_set_cache(user_cng) - cng = User.info_changeset(user, %{info: info}) - user = Repo.update!(cng) - - IO.puts("locked status of #{nickname}: #{user.info["locked"]}") + IO.puts("Locked status of #{nickname}: #{user.info.locked}") else _ -> IO.puts("No local user #{nickname}") From c3519132dfdcd5f59c0ebe99fa8ab4b764ac4982 Mon Sep 17 00:00:00 2001 From: Rin Toshaka Date: Wed, 5 Dec 2018 16:44:15 +0100 Subject: [PATCH 17/44] Sorry --- lib/mix/tasks/make_moderator.ex | 7 +++++-- lib/mix/tasks/set_admin.ex | 7 +++++-- lib/mix/tasks/set_locked.ex | 9 ++++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/mix/tasks/make_moderator.ex b/lib/mix/tasks/make_moderator.ex index 9ea6c45c1..8dc0a04dd 100644 --- a/lib/mix/tasks/make_moderator.ex +++ b/lib/mix/tasks/make_moderator.ex @@ -22,8 +22,11 @@ def run([nickname | rest]) do with %User{local: true} = user <- User.get_by_nickname(nickname) do info_cng = User.Info.admin_api_update(user.info, %{is_moderator: !!moderator}) - user_cng = Ecto.Changeset.change(user) - |> put_embed(:info, info_cng) + + user_cng = + Ecto.Changeset.change(user) + |> put_embed(:info, info_cng) + {:ok, user} = User.update_and_set_cache(user_cng) IO.puts("Moderator status of #{nickname}: #{user.info.is_moderator}") diff --git a/lib/mix/tasks/set_admin.ex b/lib/mix/tasks/set_admin.ex index 9788e49b9..ac26516f1 100644 --- a/lib/mix/tasks/set_admin.ex +++ b/lib/mix/tasks/set_admin.ex @@ -18,8 +18,11 @@ def run([nickname | rest]) do with %User{local: true} = user <- User.get_by_nickname(nickname) do info_cng = User.Info.admin_api_update(user.info, %{is_admin: !!admin}) - user_cng = Ecto.Changeset.change(user) - |> put_embed(:info, info_cng) + + user_cng = + Ecto.Changeset.change(user) + |> put_embed(:info, info_cng) + {:ok, user} = User.update_and_set_cache(user_cng) IO.puts("Admin status of #{nickname}: #{user.info.is_admin}") diff --git a/lib/mix/tasks/set_locked.ex b/lib/mix/tasks/set_locked.ex index 42d978599..e93a63505 100644 --- a/lib/mix/tasks/set_locked.ex +++ b/lib/mix/tasks/set_locked.ex @@ -8,7 +8,7 @@ defmodule Mix.Tasks.SetLocked do Example: ``mix set_locked lain`` """ - + use Mix.Task import Ecto.Changeset alias Pleroma.{Repo, User} @@ -24,8 +24,11 @@ def run([nickname | rest]) do with %User{local: true} = user <- User.get_by_nickname(nickname) do info_cng = User.Info.profile_update(user.info, %{locked: !!locked}) - user_cng = Ecto.Changeset.change(user) - |> put_embed(:info, info_cng) + + user_cng = + Ecto.Changeset.change(user) + |> put_embed(:info, info_cng) + {:ok, user} = User.update_and_set_cache(user_cng) IO.puts("Locked status of #{nickname}: #{user.info.locked}") From c4f3c5e939638905f94cdee53db7a0704a80d133 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 5 Dec 2018 20:23:28 +0300 Subject: [PATCH 18/44] [#210] Stylistic change. --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 39692163f..4eb9d96ab 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -574,7 +574,7 @@ def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do def upload(file, opts \\ []) do with {:ok, data} <- Upload.store(file, opts) do - obj_data = if opts[:actor], do: Map.put(data, "actor", opts[:actor]), else: data + obj_data = (opts[:actor] && Map.put(data, "actor", opts[:actor])) || data Repo.insert(%Object{data: obj_data}) end end From 839526a9134ba85c0a45fe85740d04a54076224c Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 5 Dec 2018 19:22:40 +0100 Subject: [PATCH 19/44] 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 3b5be09f4544ab1b3f6821fd4bbe047f94ef71ac Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 5 Dec 2018 21:48:21 +0300 Subject: [PATCH 20/44] [#210] Stylistic change. --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 4eb9d96ab..39692163f 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -574,7 +574,7 @@ def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do def upload(file, opts \\ []) do with {:ok, data} <- Upload.store(file, opts) do - obj_data = (opts[:actor] && Map.put(data, "actor", opts[:actor])) || data + obj_data = if opts[:actor], do: Map.put(data, "actor", opts[:actor]), else: data Repo.insert(%Object{data: obj_data}) end end From 3ccfe226c0e7710c2321b19643a43fcc6458a1e9 Mon Sep 17 00:00:00 2001 From: Vald Date: Thu, 6 Dec 2018 01:05:41 +0530 Subject: [PATCH 21/44] added data attrs for user and tag --- lib/pleroma/formatter.ex | 10 +++++++--- lib/pleroma/html.ex | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index 1a5c07c8a..5b03e9aeb 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -114,7 +114,7 @@ def add_user_links({subs, text}, mentions) do subs = subs ++ - Enum.map(mentions, fn {match, %User{ap_id: ap_id, info: info}, uuid} -> + Enum.map(mentions, fn {match, %User{id: id, ap_id: ap_id, info: info}, uuid} -> ap_id = if is_binary(info.source_data["url"]) do info.source_data["url"] @@ -125,7 +125,7 @@ def add_user_links({subs, text}, mentions) do short_match = String.split(match, "@") |> tl() |> hd() {uuid, - "@#{short_match}"} + "@#{short_match}"} end) {subs, uuid_text} @@ -147,7 +147,11 @@ def add_hashtag_links({subs, text}, tags) do subs = subs ++ Enum.map(tags, fn {tag_text, tag, uuid} -> - url = "" + url = + "" + {uuid, url} end) diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index 1b920d7fd..271a48b57 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -86,7 +86,7 @@ defmodule Pleroma.HTML.Scrubber.Default do Meta.remove_cdata_sections_before_scrub() Meta.strip_comments() - Meta.allow_tag_with_uri_attributes("a", ["href"], @valid_schemes) + Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes) Meta.allow_tag_with_these_attributes("a", ["name", "title"]) Meta.allow_tag_with_these_attributes("abbr", ["title"]) From 812b20d49f1ef31cf1582e17c74ef68cacd8d912 Mon Sep 17 00:00:00 2001 From: Vald Date: Thu, 6 Dec 2018 01:27:08 +0530 Subject: [PATCH 22/44] resolved formatter_test failures --- test/formatter_test.exs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/formatter_test.exs b/test/formatter_test.exs index 5d745510f..84d826295 100644 --- a/test/formatter_test.exs +++ b/test/formatter_test.exs @@ -15,7 +15,7 @@ test "turns hashtags into links" do text = "I love #cofe and #2hu" expected_text = - "I love and " + "I love and " tags = Formatter.parse_tags(text) @@ -128,9 +128,9 @@ test "gives a replacement for user links" do Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end) expected_text = - "@gsimg According to @gsimg According to @archaeme, that is @daggsy. Also hello @archaeme, that is @daggsy. Also hello @archaeme" @@ -150,7 +150,7 @@ test "gives a replacement for user links when the user is using Osada" do Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end) expected_text = - "@mike test" + "@mike test" assert expected_text == Formatter.finalize({subs, text}) end @@ -166,7 +166,7 @@ test "gives a replacement for single-character local nicknames" do assert length(subs) == 1 Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end) - expected_text = "@o hi" + expected_text = "@o hi" assert expected_text == Formatter.finalize({subs, text}) end From 743a09132bb6f9d3ecd6404e4a20e6a08598d8a7 Mon Sep 17 00:00:00 2001 From: Vald Date: Thu, 6 Dec 2018 01:28:52 +0530 Subject: [PATCH 23/44] linting --- test/formatter_test.exs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/formatter_test.exs b/test/formatter_test.exs index 84d826295..abb9d882c 100644 --- a/test/formatter_test.exs +++ b/test/formatter_test.exs @@ -128,11 +128,11 @@ test "gives a replacement for user links" do Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end) expected_text = - "@gsimg According to @archaeme, that is @daggsy. Also hello @archaeme" + "@gsimg According to @archaeme, that is @daggsy. Also hello @archaeme" assert expected_text == Formatter.finalize({subs, text}) end @@ -166,7 +166,9 @@ test "gives a replacement for single-character local nicknames" do assert length(subs) == 1 Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end) - expected_text = "@o hi" + expected_text = + "@o hi" + assert expected_text == Formatter.finalize({subs, text}) end From fdac215091332d5f7df818855b62e600870d6786 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 5 Dec 2018 21:14:06 +0100 Subject: [PATCH 24/44] 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 911a877576f1f2036fe2c7a7f22cf634ca3e5234 Mon Sep 17 00:00:00 2001 From: Vald Date: Thu, 6 Dec 2018 01:46:30 +0530 Subject: [PATCH 25/44] resolved api and controller test failures --- test/web/mastodon_api/mastodon_api_controller_test.exs | 6 +++--- test/web/twitter_api/twitter_api_controller_test.exs | 2 +- test/web/twitter_api/twitter_api_test.exs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 7cd98cde8..eb483afdf 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -590,7 +590,7 @@ test "list of notifications", %{conn: conn} do |> get("/api/v1/notifications") expected_response = - "hi @#{user.nickname}" + "hi @#{user.nickname}" assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200) assert response == expected_response @@ -611,7 +611,7 @@ test "getting a single notification", %{conn: conn} do |> get("/api/v1/notifications/#{notification.id}") expected_response = - "hi @#{user.nickname}" + "hi @#{user.nickname}" assert %{"status" => %{"content" => response}} = json_response(conn, 200) assert response == expected_response @@ -1271,7 +1271,7 @@ test "updates the user's bio", %{conn: conn} do assert user = json_response(conn, 200) assert user["note"] == - "I drink #cofe with #cofe with @#{user2.nickname}" end diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index a6495ffc1..e799a0518 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -969,7 +969,7 @@ test "it updates a user's profile", %{conn: conn} do assert user.name == "new name" assert user.bio == - "hi @#{user2.nickname}" + "hi @#{user2.nickname}" assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user}) end diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index 28230699f..76de68783 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -10,7 +10,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do test "create a status" do user = insert(:user) - _mentioned_user = insert(:user, %{nickname: "shp", ap_id: "shp"}) + mentioned_user = insert(:user, %{nickname: "shp", ap_id: "shp"}) object_data = %{ "type" => "Image", @@ -35,7 +35,7 @@ test "create a status" do {:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input) expected_text = - "Hello again, @shp.<script></script>
This is on another :moominmamma: line.
image.jpg" + "Hello again, @shp.<script></script>
This is on another :moominmamma: line.
image.jpg" assert get_in(activity.data, ["object", "content"]) == expected_text assert get_in(activity.data, ["object", "type"]) == "Note" @@ -281,7 +281,7 @@ test "it registers a new user and parses mentions in the bio" do {:ok, user2} = TwitterAPI.register_user(data2) expected_text = - "@john test" + "@john test" assert user2.bio == expected_text end From 028904c33321a17f0f04c1a43dcf3eaeeeb80c1c Mon Sep 17 00:00:00 2001 From: Vald Date: Thu, 6 Dec 2018 01:46:53 +0530 Subject: [PATCH 26/44] lint --- test/web/mastodon_api/mastodon_api_controller_test.exs | 6 +++--- test/web/twitter_api/twitter_api_controller_test.exs | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index eb483afdf..b5da2044a 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -1271,9 +1271,9 @@ test "updates the user's bio", %{conn: conn} do assert user = json_response(conn, 200) assert user["note"] == - "I drink #cofe with @#{user2.nickname}" + "I drink #cofe with @#{user2.nickname}" end test "updates the user's locking status", %{conn: conn} do diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index e799a0518..77a89ba06 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -969,7 +969,9 @@ test "it updates a user's profile", %{conn: conn} do assert user.name == "new name" assert user.bio == - "hi @#{user2.nickname}" + "hi @#{ + user2.nickname + }" assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user}) end From 3c8ffe7ed326f4c0bc515f35c905b405e90391b0 Mon Sep 17 00:00:00 2001 From: Vald Date: Thu, 6 Dec 2018 01:54:04 +0530 Subject: [PATCH 27/44] resolved activity view test --- test/web/twitter_api/views/activity_view_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/web/twitter_api/views/activity_view_test.exs b/test/web/twitter_api/views/activity_view_test.exs index 5cef06f88..bc36b0e90 100644 --- a/test/web/twitter_api/views/activity_view_test.exs +++ b/test/web/twitter_api/views/activity_view_test.exs @@ -47,7 +47,7 @@ test "a create activity with a note" do "repeated" => false, "statusnet_conversation_id" => convo_id, "statusnet_html" => - "Hey @shp!", + "Hey @shp!", "tags" => [], "text" => "Hey @shp!", "uri" => activity.data["object"]["id"], From 3ea4476445a5e9b6ec1625d7caa537f79254e9d0 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 5 Dec 2018 21:25:06 +0100 Subject: [PATCH 28/44] 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 29/44] 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 From 194869c7db1d31b139254d3a0c6a449cee0068fe Mon Sep 17 00:00:00 2001 From: Vald Date: Thu, 6 Dec 2018 02:14:56 +0530 Subject: [PATCH 30/44] added data attrs to twitter scrubber --- lib/pleroma/html.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index 271a48b57..5daaa5e69 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -45,7 +45,7 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do Meta.strip_comments() # links - Meta.allow_tag_with_uri_attributes("a", ["href"], @valid_schemes) + Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes) Meta.allow_tag_with_these_attributes("a", ["name", "title"]) # paragraphs and linebreaks From 27792b2d77564717e7d4063ab3524ab9e1cb1fb6 Mon Sep 17 00:00:00 2001 From: Hakaba Hitoyo Date: Thu, 6 Dec 2018 11:23:15 +0900 Subject: [PATCH 31/44] remove pool and timeout options which duplicate with the default --- lib/pleroma/http/connection.ex | 6 +++++- lib/pleroma/web/activity_pub/activity_pub.ex | 4 +--- lib/pleroma/web/ostatus/ostatus.ex | 9 ++------- lib/pleroma/web/salmon/salmon.ex | 7 +------ lib/pleroma/web/websub/websub.ex | 5 ----- 5 files changed, 9 insertions(+), 22 deletions(-) diff --git a/lib/pleroma/http/connection.ex b/lib/pleroma/http/connection.ex index 5e8f2aabd..66e0d0568 100644 --- a/lib/pleroma/http/connection.ex +++ b/lib/pleroma/http/connection.ex @@ -3,7 +3,11 @@ defmodule Pleroma.HTTP.Connection do Connection for http-requests. """ - @hackney_options [pool: :default] + @hackney_options [ + pool: :default, + timeout: 10000, + recv_timeout: 20000 + ] @adapter Application.get_env(:tesla, :adapter) @doc """ diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 60253a715..03a607f83 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -766,9 +766,7 @@ def fetch_and_contain_remote_object_from_id(id) do @httpoison.get( id, [Accept: "application/activity+json"], - follow_redirect: true, - timeout: 10000, - recv_timeout: 20000 + follow_redirect: true ), {:ok, data} <- Jason.decode(body), :ok <- Transmogrifier.contain_origin_from_id(id, data) do diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 67df354db..bc14b8785 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -350,11 +350,7 @@ def fetch_activity_from_atom_url(url) do @httpoison.get( url, [Accept: "application/atom+xml"], - follow_redirect: true, - adapter: [ - timeout: 10000, - recv_timeout: 20000 - ] + follow_redirect: true ) do Logger.debug("Got document from #{url}, handling...") handle_incoming(body) @@ -369,8 +365,7 @@ def fetch_activity_from_html_url(url) do Logger.debug("Trying to fetch #{url}") with true <- String.starts_with?(url, "http"), - {:ok, %{body: body}} <- - @httpoison.get(url, [], follow_redirect: true, timeout: 10000, recv_timeout: 20000), + {:ok, %{body: body}} <- @httpoison.get(url, [], follow_redirect: true), {:ok, atom_url} <- get_atom_url(body) do fetch_activity_from_atom_url(atom_url) else diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 97251c05e..0e2cfddd0 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -162,12 +162,7 @@ defp send_to_user(%{info: %{salmon: salmon}}, feed, poster) do poster.( salmon, feed, - [{"Content-Type", "application/magic-envelope+xml"}], - adapter: [ - timeout: 10000, - recv_timeout: 20000, - pool: :default - ] + [{"Content-Type", "application/magic-envelope+xml"}] ) do Logger.debug(fn -> "Pushed to #{salmon}, code #{code}" end) else diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index 0761b5475..8cb07006f 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -264,11 +264,6 @@ def publish_one(%{xml: xml, topic: topic, callback: callback, secret: secret}) d [ {"Content-Type", "application/atom+xml"}, {"X-Hub-Signature", "sha1=#{signature}"} - ], - adapter: [ - timeout: 10000, - recv_timeout: 20000, - pool: :default ] ) do Logger.info(fn -> "Pushed to #{callback}, code #{code}" end) From 96ba95df2e6690c1c6a58cf087c91eab7af728b4 Mon Sep 17 00:00:00 2001 From: Hakaba Hitoyo Date: Thu, 6 Dec 2018 11:38:33 +0900 Subject: [PATCH 32/44] remove follow_redirect options --- lib/pleroma/http/connection.ex | 3 ++- lib/pleroma/reverse_proxy.ex | 2 +- lib/pleroma/web/activity_pub/activity_pub.ex | 3 +-- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 1 - lib/pleroma/web/ostatus/ostatus.ex | 5 ++--- lib/pleroma/web/web_finger/web_finger.ex | 2 +- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/pleroma/http/connection.ex b/lib/pleroma/http/connection.ex index 66e0d0568..db46f9e55 100644 --- a/lib/pleroma/http/connection.ex +++ b/lib/pleroma/http/connection.ex @@ -6,7 +6,8 @@ defmodule Pleroma.HTTP.Connection do @hackney_options [ pool: :default, timeout: 10000, - recv_timeout: 20000 + recv_timeout: 20000, + follow_redirect: true ] @adapter Application.get_env(:tesla, :adapter) diff --git a/lib/pleroma/reverse_proxy.ex b/lib/pleroma/reverse_proxy.ex index ad9dc82fe..4ca84152a 100644 --- a/lib/pleroma/reverse_proxy.ex +++ b/lib/pleroma/reverse_proxy.ex @@ -56,7 +56,7 @@ defmodule Pleroma.ReverseProxy do @hackney Application.get_env(:pleroma, :hackney, :hackney) @httpoison Application.get_env(:pleroma, :httpoison, HTTPoison) - @default_hackney_options [{:follow_redirect, true}] + @default_hackney_options [] @inline_content_types [ "image/gif", diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 03a607f83..922ffb946 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -765,8 +765,7 @@ def fetch_and_contain_remote_object_from_id(id) do {:ok, %{body: body, status: code}} when code in 200..299 <- @httpoison.get( id, - [Accept: "application/activity+json"], - follow_redirect: true + Accept: "application/activity+json" ), {:ok, data} <- Jason.decode(body), :ok <- Transmogrifier.contain_origin_from_id(id, data) do diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 300bdc04a..fc4f3fdc7 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1185,7 +1185,6 @@ def suggestions(%{assigns: %{user: user}} = conn, _) do @httpoison.get( url, [], - follow_redirect: true, adapter: [ timeout: timeout, recv_timeout: timeout diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index bc14b8785..9f33cd5cd 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -349,8 +349,7 @@ def fetch_activity_from_atom_url(url) do {:ok, %{body: body, status: code}} when code in 200..299 <- @httpoison.get( url, - [Accept: "application/atom+xml"], - follow_redirect: true + Accept: "application/atom+xml" ) do Logger.debug("Got document from #{url}, handling...") handle_incoming(body) @@ -365,7 +364,7 @@ def fetch_activity_from_html_url(url) do Logger.debug("Trying to fetch #{url}") with true <- String.starts_with?(url, "http"), - {:ok, %{body: body}} <- @httpoison.get(url, [], follow_redirect: true), + {:ok, %{body: body}} <- @httpoison.get(url, []), {:ok, atom_url} <- get_atom_url(body) do fetch_activity_from_atom_url(atom_url) else diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex index 99c65a6bf..0ff3b8b5f 100644 --- a/lib/pleroma/web/web_finger/web_finger.ex +++ b/lib/pleroma/web/web_finger/web_finger.ex @@ -221,7 +221,7 @@ def get_template_from_xml(body) do def find_lrdd_template(domain) do with {:ok, %{status: status, body: body}} when status in 200..299 <- - @httpoison.get("http://#{domain}/.well-known/host-meta", [], follow_redirect: true) do + @httpoison.get("http://#{domain}/.well-known/host-meta", []) do get_template_from_xml(body) else _ -> From 3e90f688f14310e92fe9343f2680c58d74f71cb6 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 6 Dec 2018 10:26:17 +0300 Subject: [PATCH 33/44] [#210] Mastodon: actor storing for media uploads, ownership check to update_media. Refactoring. --- lib/pleroma/object.ex | 9 ++++- lib/pleroma/web/activity_pub/activity_pub.ex | 8 ++++- .../mastodon_api/mastodon_api_controller.ex | 34 +++++++++---------- lib/pleroma/web/twitter_api/twitter_api.ex | 6 +--- .../web/twitter_api/twitter_api_controller.ex | 2 +- .../mastodon_api_controller_test.exs | 6 +++- 6 files changed, 38 insertions(+), 27 deletions(-) diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 03a75dfbd..31c8dd5bd 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -1,6 +1,6 @@ defmodule Pleroma.Object do use Ecto.Schema - alias Pleroma.{Repo, Object, Activity} + alias Pleroma.{Repo, Object, User, Activity} import Ecto.{Query, Changeset} schema "objects" do @@ -31,6 +31,13 @@ def normalize(obj) when is_map(obj), do: Object.get_by_ap_id(obj["id"]) def normalize(ap_id) when is_binary(ap_id), do: Object.get_by_ap_id(ap_id) def normalize(_), do: nil + # Owned objects can only be mutated by their owner + def authorize_mutation(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}), + do: actor == ap_id + + # Legacy objects can be mutated by anybody + def authorize_mutation(%Object{}, %User{}), do: true + if Mix.env() == :test do def get_cached_by_ap_id(ap_id) do get_by_ap_id(ap_id) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 39692163f..aaf9d3854 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -574,7 +574,13 @@ def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do def upload(file, opts \\ []) do with {:ok, data} <- Upload.store(file, opts) do - obj_data = if opts[:actor], do: Map.put(data, "actor", opts[:actor]), else: data + obj_data = + if opts[:actor] do + Map.put(data, "actor", opts[:actor]) + else + data + end + Repo.insert(%Object{data: obj_data}) end end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 543fdf416..ef204f7f3 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -433,33 +433,31 @@ def relationships(%{assigns: %{user: user}} = conn, _) do |> json([]) end - def update_media(%{assigns: %{user: _}} = conn, data) do + def update_media(%{assigns: %{user: user}} = conn, data) do with %Object{} = object <- Repo.get(Object, data["id"]), + true <- Object.authorize_mutation(object, user), true <- is_binary(data["description"]), description <- data["description"] do new_data = %{object.data | "name" => description} - change = Object.change(object, %{data: new_data}) - {:ok, _} = Repo.update(change) + {:ok, _} = + object + |> Object.change(%{data: new_data}) + |> Repo.update() - data = - new_data - |> Map.put("id", object.id) - - render(conn, StatusView, "attachment.json", %{attachment: data}) + attachment_data = Map.put(new_data, "id", object.id) + render(conn, StatusView, "attachment.json", %{attachment: attachment_data}) end end - def upload(%{assigns: %{user: _}} = conn, %{"file" => file} = data) do - with {:ok, object} <- ActivityPub.upload(file, description: Map.get(data, "description")) do - change = Object.change(object, %{data: object.data}) - {:ok, object} = Repo.update(change) - - objdata = - object.data - |> Map.put("id", object.id) - - render(conn, StatusView, "attachment.json", %{attachment: objdata}) + def upload(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do + with {:ok, object} <- + ActivityPub.upload(file, + actor: User.ap_id(user), + description: Map.get(data, "description") + ) do + attachment_data = Map.put(object.data, "id", object.id) + render(conn, StatusView, "attachment.json", %{attachment: attachment_data}) end end diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index b9468ab03..9c485d965 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -93,12 +93,8 @@ def unfav(%User{} = user, ap_id_or_id) do end end - def ap_upload(%Plug.Upload{} = file, %User{} = user) do - ActivityPub.upload(file, actor: User.ap_id(user)) - end - def upload(%Plug.Upload{} = file, %User{} = user, format \\ "xml") do - {:ok, object} = ap_upload(file, user) + {:ok, object} = ActivityPub.upload(file, actor: User.ap_id(user)) url = List.first(object.data["url"]) href = url["href"] diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 2f12131e7..c19ee230f 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -239,7 +239,7 @@ def update_media(%{assigns: %{user: user}} = conn, %{"media_id" => id} = data) d !object -> {halt(conn), :not_found, ""} - object.data["actor"] != User.ap_id(user) -> + !Object.authorize_mutation(object, user) -> {halt(conn), :forbidden, "You can only update your own uploads."} !is_binary(description) -> diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index b5839cff1..d952cecc8 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -2,7 +2,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do use Pleroma.Web.ConnCase alias Pleroma.Web.TwitterAPI.TwitterAPI - alias Pleroma.{Repo, User, Activity, Notification} + alias Pleroma.{Repo, User, Object, Activity, Notification} alias Pleroma.Web.{OStatus, CommonAPI} alias Pleroma.Web.ActivityPub.ActivityPub @@ -959,6 +959,10 @@ test "media upload", %{conn: conn} do assert media["type"] == "image" assert media["description"] == desc + assert media["id"] + + object = Repo.get(Object, media["id"]) + assert object.data["actor"] == User.ap_id(user) end test "hashtag timeline", %{conn: conn} do From a09ed0f5afe6336763b47edab0c0d9a629a74ad0 Mon Sep 17 00:00:00 2001 From: Hakaba Hitoyo Date: Thu, 6 Dec 2018 18:41:29 +0900 Subject: [PATCH 34/44] avoid mix format bug --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- lib/pleroma/web/ostatus/ostatus.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 922ffb946..a1637ccad 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -765,7 +765,7 @@ def fetch_and_contain_remote_object_from_id(id) do {:ok, %{body: body, status: code}} when code in 200..299 <- @httpoison.get( id, - Accept: "application/activity+json" + [{:Accept, "application/activity+json"}] ), {:ok, data} <- Jason.decode(body), :ok <- Transmogrifier.contain_origin_from_id(id, data) do diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 9f33cd5cd..53d71440e 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -349,7 +349,7 @@ def fetch_activity_from_atom_url(url) do {:ok, %{body: body, status: code}} when code in 200..299 <- @httpoison.get( url, - Accept: "application/atom+xml" + [{:Accept, "application/atom+xml"}] ) do Logger.debug("Got document from #{url}, handling...") handle_incoming(body) From 6a6aaa0e1aade45bc2e3b68ee2c4686cc80f46cd Mon Sep 17 00:00:00 2001 From: scarlett Date: Thu, 6 Dec 2018 11:37:29 +0000 Subject: [PATCH 35/44] Use object.normalize. --- lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex index 3f216010e..06fafb3ee 100644 --- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex +++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex @@ -1,5 +1,5 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do - alias Pleroma.Activity + alias Pleroma.Object @behaviour Pleroma.Web.ActivityPub.MRF @@ -20,11 +20,11 @@ def filter_by_summary(parent, child), do: child def filter(%{"type" => activity_type} = object) when activity_type == "Create" do child = object["object"] - in_reply_to = Activity.get_create_activity_by_object_ap_id(child["inReplyTo"]) + in_reply_to = Object.normalize(child["inReplyTo"]) child = if(in_reply_to, - do: filter_by_summary(in_reply_to.data["object"], child), + do: filter_by_summary(in_reply_to.data, child), else: child ) From 04a48286e69704bf83429b85dbcdb70863bdcff1 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 6 Dec 2018 19:29:04 +0700 Subject: [PATCH 36/44] Add web push support --- lib/mix/tasks/generate_config.ex | 6 +- lib/mix/tasks/sample_config.eex | 6 + lib/pleroma/application.ex | 3 +- lib/pleroma/notification.ex | 1 + .../mastodon_api/mastodon_api_controller.ex | 6 +- lib/pleroma/web/push/push.ex | 126 ++++++++++++++++++ lib/pleroma/web/push/subscription.ex | 2 +- .../controllers/util_controller.ex | 6 +- mix.exs | 3 +- mix.lock | 3 + 10 files changed, 155 insertions(+), 7 deletions(-) create mode 100644 lib/pleroma/web/push/push.ex diff --git a/lib/mix/tasks/generate_config.ex b/lib/mix/tasks/generate_config.ex index 70a110561..58ce3113b 100644 --- a/lib/mix/tasks/generate_config.ex +++ b/lib/mix/tasks/generate_config.ex @@ -14,6 +14,8 @@ def run(_) do resultSql = EEx.eval_file("lib/mix/tasks/sample_psql.eex", dbpass: dbpass) + {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1) + result = EEx.eval_file( "lib/mix/tasks/sample_config.eex", @@ -21,7 +23,9 @@ def run(_) do email: email, name: name, secret: secret, - dbpass: dbpass + dbpass: dbpass, + web_push_public_key: Base.url_encode64(web_push_public_key, padding: false), + web_push_private_key: Base.url_encode64(web_push_private_key, padding: false) ) IO.puts( diff --git a/lib/mix/tasks/sample_config.eex b/lib/mix/tasks/sample_config.eex index 3881ead26..f2272b10a 100644 --- a/lib/mix/tasks/sample_config.eex +++ b/lib/mix/tasks/sample_config.eex @@ -25,6 +25,12 @@ config :pleroma, Pleroma.Repo, hostname: "localhost", pool_size: 10 +# Configure web push notifications +config :web_push_encryption, :vapid_details, + subject: "mailto:<%= email %>", + public_key: "<%= web_push_public_key %>", + private_key: "<%= web_push_private_key %>" + # Configure S3 support if desired. # The public S3 endpoint is different depending on region and provider, # consult your S3 provider's documentation for details on what to use. diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index a89728471..565e938fd 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -41,7 +41,8 @@ def start(_type, _args) do ), worker(Pleroma.Web.Federator, []), worker(Pleroma.Gopher.Server, []), - worker(Pleroma.Stats, []) + worker(Pleroma.Stats, []), + worker(Pleroma.Web.Push, []) ] ++ if Mix.env() == :test, do: [], diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index e0dcd9823..6163413c8 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -96,6 +96,7 @@ def create_notification(%Activity{} = activity, %User{} = user) do notification = %Notification{user_id: user.id, activity: activity} {:ok, notification} = Repo.insert(notification) Pleroma.Web.Streamer.stream("user", notification) + Pleroma.Web.Push.send(notification) notification end end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 7f06ee607..de5b2696f 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1138,6 +1138,8 @@ def delete_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do json(conn, %{}) end + alias Pleroma.Web.MastodonAPI.PushSubscriptionView + def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do Pleroma.Web.Push.Subscription.delete_if_exists(user, token) {:ok, subscription} = Pleroma.Web.Push.Subscription.create(user, token, params) @@ -1145,7 +1147,7 @@ def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, par json(conn, view) end - def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do + def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do subscription = Pleroma.Web.Push.Subscription.get(user, token) view = PushSubscriptionView.render("push_subscription.json", subscription: subscription) json(conn, view) @@ -1160,7 +1162,7 @@ def update_push_subscription( json(conn, view) end - def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do + def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do {:ok, _response} = Pleroma.Web.Push.Subscription.delete(user, token) json(conn, %{}) end diff --git a/lib/pleroma/web/push/push.ex b/lib/pleroma/web/push/push.ex new file mode 100644 index 000000000..d27750ab6 --- /dev/null +++ b/lib/pleroma/web/push/push.ex @@ -0,0 +1,126 @@ +defmodule Pleroma.Web.Push do + use GenServer + + alias Pleroma.{Repo, User} + alias Pleroma.Web.Push.Subscription + + require Logger + import Ecto.Query + + @types ["Create", "Follow", "Announce", "Like"] + + @gcm_api_key nil + + def start_link() do + GenServer.start_link(__MODULE__, :ok, name: __MODULE__) + end + + def init(:ok) do + case Application.get_env(:web_push_encryption, :vapid_details) do + nil -> + Logger.error( + "VAPID key pair is not found. Please, add VAPID configuration to config. Run `mix web_push.gen.keypair` mix task to create a key pair" + ) + + {:error, %{}} + + _ -> + {:ok, %{}} + end + end + + def send(notification) do + GenServer.cast(Pleroma.Web.Push, {:send, notification}) + end + + def handle_cast( + {:send, %{activity: %{data: %{"type" => type}}, user_id: user_id} = notification}, + state + ) + when type in @types do + actor = User.get_cached_by_ap_id(notification.activity.data["actor"]) + body = notification |> format(actor) |> Jason.encode!() + + Subscription + |> where(user_id: ^user_id) + |> Repo.all() + |> Enum.each(fn record -> + subscription = %{ + keys: %{ + p256dh: record.key_p256dh, + auth: record.key_auth + }, + endpoint: record.endpoint + } + + case WebPushEncryption.send_web_push(body, subscription, @gcm_api_key) do + {:ok, %{status_code: code}} when 400 <= code and code < 500 -> + Logger.debug("Removing subscription record") + Repo.delete!(record) + :ok + + {:ok, %{status_code: code}} when 200 <= code and code < 300 -> + :ok + + {:ok, %{status_code: code}} -> + Logger.error("Web Push Nonification failed with code: #{code}") + :error + + data -> + Logger.error("Web Push Nonification failed with unknown error") + IO.inspect(data) + :error + end + end) + + {:noreply, state} + end + + def handle_cast({:send, _}, state) do + Logger.warn("Unknown notification type") + {:noreply, state} + end + + def format(%{activity: %{data: %{"type" => "Create"}}}, actor) do + %{ + title: "New Mention", + body: "@#{actor.nickname} has mentiond you", + icon: get_avatar_url(actor) + } + end + + def format(%{activity: %{data: %{"type" => "Follow"}}}, actor) do + %{ + title: "New Follower", + body: "@#{actor.nickname} has followed you", + icon: get_avatar_url(actor) + } + end + + def format(%{activity: %{data: %{"type" => "Announce"}}}, actor) do + %{ + title: "New Announce", + body: "@#{actor.nickname} has announced your post", + icon: get_avatar_url(actor) + } + end + + def format(%{activity: %{data: %{"type" => "Like"}}}, actor) do + %{ + title: "New Like", + body: "@#{actor.nickname} has liked your post", + icon: get_avatar_url(actor) + } + end + + def get_avatar_url(%{avatar: %{"type" => "Image", "url" => urls}}) do + case List.first(urls) do + %{"href" => url} -> url + _ -> get_avatar_url(nil) + end + end + + def get_avatar_url(_) do + Pleroma.Web.Endpoint.static_url() <> "/images/avi.png" + end +end diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex index dc8fe9f33..cfab7a98e 100644 --- a/lib/pleroma/web/push/subscription.ex +++ b/lib/pleroma/web/push/subscription.ex @@ -1,6 +1,6 @@ defmodule Pleroma.Web.Push.Subscription do use Ecto.Schema - import Ecto.{Changeset, Query} + import Ecto.Changeset alias Pleroma.{Repo, User} alias Pleroma.Web.OAuth.Token alias Pleroma.Web.Push.Subscription diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 886b70f5f..f06020a3e 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -156,13 +156,17 @@ def config(conn, _params) do |> send_resp(200, response) _ -> + vapid_public_key = + Keyword.get(Application.get_env(:web_push_encryption, :vapid_details), :public_key) + data = %{ name: Keyword.get(@instance, :name), description: Keyword.get(@instance, :description), server: Web.base_url(), textlimit: to_string(Keyword.get(@instance, :limit)), closed: if(Keyword.get(@instance, :registrations_open), do: "0", else: "1"), - private: if(Keyword.get(@instance, :public, true), do: "0", else: "1") + private: if(Keyword.get(@instance, :public, true), do: "0", else: "1"), + vapidPublicKey: vapid_public_key } pleroma_fe = %{ diff --git a/mix.exs b/mix.exs index 24c7108a0..2f2f1398c 100644 --- a/mix.exs +++ b/mix.exs @@ -52,7 +52,8 @@ defp deps do {:credo, "~> 0.9.3", only: [:dev, :test]}, {:mock, "~> 0.3.1", only: :test}, {:crypt, - git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"} + git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"}, + {:web_push_encryption, "~> 0.2.1"} ] end diff --git a/mix.lock b/mix.lock index 1da8e7b0c..4bc5d1bb2 100644 --- a/mix.lock +++ b/mix.lock @@ -1,4 +1,5 @@ %{ + "base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, "cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"}, "calendar": {:hex, :calendar, "0.17.4", "22c5e8d98a4db9494396e5727108dffb820ee0d18fed4b0aa8ab76e4f5bc32f1", [:mix], [{:tzdata, "~> 0.5.8 or ~> 0.1.201603", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"}, @@ -22,6 +23,7 @@ "httpoison": {:hex, :httpoison, "1.2.0", "2702ed3da5fd7a8130fc34b11965c8cfa21ade2f232c00b42d96d4967c39a3a3", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, "idna": {:hex, :idna, "5.1.2", "e21cb58a09f0228a9e0b95eaa1217f1bcfc31a1aaa6e1fdf2f53a33f7dbd9494", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, "jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, + "jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"}, "meck": {:hex, :meck, "0.8.9", "64c5c0bd8bcca3a180b44196265c8ed7594e16bcc845d0698ec6b4e577f48188", [:rebar3], [], "hexpm"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, "mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [:mix], [], "hexpm"}, @@ -45,4 +47,5 @@ "tzdata": {:hex, :tzdata, "0.5.17", "50793e3d85af49736701da1a040c415c97dc1caf6464112fd9bd18f425d3053b", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"}, "unsafe": {:hex, :unsafe, "1.0.0", "7c21742cd05380c7875546b023481d3a26f52df8e5dfedcb9f958f322baae305", [:mix], [], "hexpm"}, + "web_push_encryption": {:hex, :web_push_encryption, "0.2.1", "d42cecf73420d9dc0053ba3299cc8c8d6ff2be2487d67ca2a57265868e4d9a98", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, } From bac58b152495c3ebf72e3ad1c3102de075fcc366 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 6 Dec 2018 19:56:56 +0700 Subject: [PATCH 37/44] show warning if VAPID is not set --- lib/pleroma/web/push/push.ex | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/push/push.ex b/lib/pleroma/web/push/push.ex index d27750ab6..4ac3be8a3 100644 --- a/lib/pleroma/web/push/push.ex +++ b/lib/pleroma/web/push/push.ex @@ -18,11 +18,11 @@ def start_link() do def init(:ok) do case Application.get_env(:web_push_encryption, :vapid_details) do nil -> - Logger.error( + Logger.warn( "VAPID key pair is not found. Please, add VAPID configuration to config. Run `mix web_push.gen.keypair` mix task to create a key pair" ) - {:error, %{}} + :ignore _ -> {:ok, %{}} @@ -30,7 +30,9 @@ def init(:ok) do end def send(notification) do - GenServer.cast(Pleroma.Web.Push, {:send, notification}) + if Application.get_env(:web_push_encryption, :vapid_details) do + GenServer.cast(Pleroma.Web.Push, {:send, notification}) + end end def handle_cast( From 0020b22688f390fe182301311633a4b167e48e6c Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 6 Dec 2018 20:17:00 +0700 Subject: [PATCH 38/44] add VAPID to test config --- config/test.exs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/test.exs b/config/test.exs index 6f6227c20..ca10a616c 100644 --- a/config/test.exs +++ b/config/test.exs @@ -27,6 +27,12 @@ config :pleroma, :ostatus, Pleroma.Web.OStatusMock config :tesla, adapter: Tesla.Mock +config :web_push_encryption, :vapid_details, + subject: "mailto:administrator@example.com", + public_key: + "BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4", + private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA" + try do import_config "test.secret.exs" rescue From 3d492795b76cfe3b3616607f815a0effe44c1ce5 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 6 Dec 2018 20:42:00 +0700 Subject: [PATCH 39/44] clean up --- lib/pleroma/web/mastodon_api/views/push_subscription_view.ex | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex b/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex index a910bb43e..68bb45494 100644 --- a/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex +++ b/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex @@ -6,9 +6,7 @@ def render("push_subscription.json", %{subscription: subscription}) do %{ id: to_string(subscription.id), endpoint: subscription.endpoint, - alerts: Map.get(subscription.data, "alerts"), - # TODO: generate VAPID server key - server_key: "N/A" + alerts: Map.get(subscription.data, "alerts") } end end From 3dff61ebec4f4b216903d79261ec5cac80e70a08 Mon Sep 17 00:00:00 2001 From: scarlett Date: Thu, 6 Dec 2018 13:48:12 +0000 Subject: [PATCH 40/44] Harden re: detection. --- lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex index 06fafb3ee..a3b9c4616 100644 --- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex +++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex @@ -3,13 +3,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do @behaviour Pleroma.Web.ActivityPub.MRF + @have_re Regex.compile!("^re:[[:space:]]*", [:caseless]) def filter_by_summary( %{"summary" => parent_summary} = parent, %{"summary" => child_summary} = child ) - when not is_nil(child_summary) and child_summary == parent_summary and - byte_size(child_summary) > 1 do - if not String.starts_with?(child_summary, "re:") do + when not is_nil(child_summary) and byte_size(child_summary) > 0 and + not is_nil(parent_summary) and byte_size(parent_summary) > 0 do + if (child_summary == parent_summary and not Regex.match?(@have_re, child_summary)) or + (Regex.match?(@have_re, parent_summary) && + Regex.replace(@have_re, parent_summary, "") == child_summary) do Map.put(child, "summary", "re: " <> child_summary) else child From 79668c08fc566dad6ecb9d2909a23612f91d06ed Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 6 Dec 2018 20:50:20 +0700 Subject: [PATCH 41/44] cleanup --- .../web/mastodon_api/mastodon_api_controller.ex | 15 +++++++++++---- lib/pleroma/web/push/push.ex | 1 - 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index dd6b0a361..c9530c748 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -2,13 +2,22 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do use Pleroma.Web, :controller alias Pleroma.{Repo, Object, Activity, User, Notification, Stats} alias Pleroma.Web - alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView, ListView, FilterView} + + alias Pleroma.Web.MastodonAPI.{ + StatusView, + AccountView, + MastodonView, + ListView, + FilterView, + PushSubscriptionView + } + alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.CommonAPI alias Pleroma.Web.OAuth.{Authorization, Token, App} alias Pleroma.Web.MediaProxy - alias Comeonin.Pbkdf2 + import Ecto.Query require Logger @@ -1160,8 +1169,6 @@ def delete_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do json(conn, %{}) end - alias Pleroma.Web.MastodonAPI.PushSubscriptionView - def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do Pleroma.Web.Push.Subscription.delete_if_exists(user, token) {:ok, subscription} = Pleroma.Web.Push.Subscription.create(user, token, params) diff --git a/lib/pleroma/web/push/push.ex b/lib/pleroma/web/push/push.ex index 4ac3be8a3..4e9bc5741 100644 --- a/lib/pleroma/web/push/push.ex +++ b/lib/pleroma/web/push/push.ex @@ -70,7 +70,6 @@ def handle_cast( data -> Logger.error("Web Push Nonification failed with unknown error") - IO.inspect(data) :error end end) From 6f36e903b0a8702ec279df29c1d039cb08a574d4 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 6 Dec 2018 20:55:46 +0700 Subject: [PATCH 42/44] use `User.avatar_url` --- lib/pleroma/web/push/push.ex | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/lib/pleroma/web/push/push.ex b/lib/pleroma/web/push/push.ex index 4e9bc5741..5a873ec19 100644 --- a/lib/pleroma/web/push/push.ex +++ b/lib/pleroma/web/push/push.ex @@ -68,7 +68,7 @@ def handle_cast( Logger.error("Web Push Nonification failed with code: #{code}") :error - data -> + _ -> Logger.error("Web Push Nonification failed with unknown error") :error end @@ -86,7 +86,7 @@ def format(%{activity: %{data: %{"type" => "Create"}}}, actor) do %{ title: "New Mention", body: "@#{actor.nickname} has mentiond you", - icon: get_avatar_url(actor) + icon: User.avatar_url(actor) } end @@ -94,7 +94,7 @@ def format(%{activity: %{data: %{"type" => "Follow"}}}, actor) do %{ title: "New Follower", body: "@#{actor.nickname} has followed you", - icon: get_avatar_url(actor) + icon: User.avatar_url(actor) } end @@ -102,7 +102,7 @@ def format(%{activity: %{data: %{"type" => "Announce"}}}, actor) do %{ title: "New Announce", body: "@#{actor.nickname} has announced your post", - icon: get_avatar_url(actor) + icon: User.avatar_url(actor) } end @@ -110,18 +110,7 @@ def format(%{activity: %{data: %{"type" => "Like"}}}, actor) do %{ title: "New Like", body: "@#{actor.nickname} has liked your post", - icon: get_avatar_url(actor) + icon: User.avatar_url(actor) } end - - def get_avatar_url(%{avatar: %{"type" => "Image", "url" => urls}}) do - case List.first(urls) do - %{"href" => url} -> url - _ -> get_avatar_url(nil) - end - end - - def get_avatar_url(_) do - Pleroma.Web.Endpoint.static_url() <> "/images/avi.png" - end end From d27e3f269f5e93efd91397ada72656120be3db41 Mon Sep 17 00:00:00 2001 From: scarlett Date: Thu, 6 Dec 2018 14:00:41 +0000 Subject: [PATCH 43/44] Rename regular expression 'have_re' to 'reply_prefix'. --- lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex index a3b9c4616..c8c74ede6 100644 --- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex +++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex @@ -3,16 +3,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do @behaviour Pleroma.Web.ActivityPub.MRF - @have_re Regex.compile!("^re:[[:space:]]*", [:caseless]) + @reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless]) def filter_by_summary( %{"summary" => parent_summary} = parent, %{"summary" => child_summary} = child ) when not is_nil(child_summary) and byte_size(child_summary) > 0 and not is_nil(parent_summary) and byte_size(parent_summary) > 0 do - if (child_summary == parent_summary and not Regex.match?(@have_re, child_summary)) or - (Regex.match?(@have_re, parent_summary) && - Regex.replace(@have_re, parent_summary, "") == child_summary) do + if (child_summary == parent_summary and not Regex.match?(@reply_prefix, child_summary)) or + (Regex.match?(@reply_prefix, parent_summary) && + Regex.replace(@reply_prefix, parent_summary, "") == child_summary) do Map.put(child, "summary", "re: " <> child_summary) else child From 2ae1128d9f7a89aff6ba7fb3d486a00f76dbc28b Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 6 Dec 2018 17:42:07 +0300 Subject: [PATCH 44/44] MastoAPI: Fix put_settings --- lib/pleroma/user.ex | 4 ---- lib/pleroma/user/info.ex | 5 +++++ lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 8 +++++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 74ae5ef0d..9da674982 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -62,10 +62,6 @@ def follow_changeset(struct, params \\ %{}) do |> validate_required([:following]) end - def info_changeset(struct, params \\ %{}) do - raise "NOT VALID ANYMORE" - end - def user_info(%User{} = user) do oneself = if user.local, do: 1, else: 0 diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 7a99787f8..d81b45b8d 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -149,6 +149,11 @@ def mastodon_profile_update(info, params) do ]) end + def mastodon_settings_update(info, params) do + info + |> cast(params, [:settings]) + end + def set_source_data(info, source_data) do params = %{source_data: source_data} diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index eecfc742b..e8dd9db15 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -970,9 +970,11 @@ def index(%{assigns: %{user: user}} = conn, _params) do end def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do - with new_info <- Map.put(user.info, "settings", settings), - change <- User.info_changeset(user, %{info: new_info}), - {:ok, _user} <- User.update_and_set_cache(change) do + info_cng = User.Info.mastodon_settings_update(user.info, settings) + + with changeset <- User.update_changeset(user), + changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng), + {:ok, user} <- User.update_and_set_cache(changeset) do conn |> json(%{}) else