From 61889e00fc4a77e92ed7af3b6a270d10d5b2f34b Mon Sep 17 00:00:00 2001 From: rinpatch Date: Wed, 29 Apr 2020 14:26:31 +0300 Subject: Deactivate local users on deletion instead of deleting the record Prevents the possibility of re-registration, which allowed to read DMs of the deleted account. Also includes a migration that tries to find any already deleted accounts and insert skeletons for them. Closes pleroma/pleroma#1687 --- lib/pleroma/user.ex | 11 +++++- .../controllers/pleroma_api_controller.ex | 5 ++- ...28221338_insert_skeletons_for_deleted_users.exs | 45 ++++++++++++++++++++++ test/tasks/user_test.exs | 2 +- test/user_test.exs | 14 +------ test/web/activity_pub/transmogrifier_test.exs | 3 +- 6 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 priv/repo/migrations/20200428221338_insert_skeletons_for_deleted_users.exs diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 0115abed5..0e5121694 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1416,8 +1416,15 @@ def perform(:delete, %User{} = user) do end) delete_user_activities(user) - invalidate_cache(user) - Repo.delete(user) + + if user.local do + user + |> change(%{deactivated: true, email: nil}) + |> update_and_set_cache() + else + invalidate_cache(user) + Repo.delete(user) + end end def perform(:deactivate_async, user, status), do: deactivate(user, status) diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index dae7f0f2f..41677d04d 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -53,7 +53,10 @@ def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id} else users = Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1) - |> Enum.filter(& &1) + |> Enum.filter(fn + %{deactivated: false} -> true + _ -> false + end) %{ name: emoji, diff --git a/priv/repo/migrations/20200428221338_insert_skeletons_for_deleted_users.exs b/priv/repo/migrations/20200428221338_insert_skeletons_for_deleted_users.exs new file mode 100644 index 000000000..11d9a70ba --- /dev/null +++ b/priv/repo/migrations/20200428221338_insert_skeletons_for_deleted_users.exs @@ -0,0 +1,45 @@ +defmodule Pleroma.Repo.Migrations.InsertSkeletonsForDeletedUsers do + use Ecto.Migration + + alias Pleroma.User + alias Pleroma.Repo + + import Ecto.Query + + def change do + Application.ensure_all_started(:flake_id) + + local_ap_id = + User.Query.build(%{local: true}) + |> select([u], u.ap_id) + |> limit(1) + |> Repo.one() + + unless local_ap_id == nil do + # Hack to get instance base url because getting it from Phoenix + # would require starting the whole application + instance_uri = + local_ap_id + |> URI.parse() + |> Map.put(:query, nil) + |> Map.put(:path, nil) + |> URI.to_string() + + {:ok, %{rows: ap_ids}} = + Ecto.Adapters.SQL.query( + Repo, + "select distinct unnest(nonexistent_locals.recipients) from activities, lateral (select array_agg(recipient) as recipients from unnest(activities.recipients) as recipient where recipient similar to '#{ + instance_uri + }/users/[A-Za-z0-9]*' and not(recipient in (select ap_id from users where local = true))) nonexistent_locals;", + [], + timeout: :infinity + ) + + ap_ids + |> Enum.each(fn [ap_id] -> + Ecto.Changeset.change(%User{}, deactivated: true, ap_id: ap_id) + |> Repo.insert() + end) + end + end +end diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs index b45f37263..22030a423 100644 --- a/test/tasks/user_test.exs +++ b/test/tasks/user_test.exs @@ -92,7 +92,7 @@ test "user is deleted" do assert_received {:mix_shell, :info, [message]} assert message =~ " deleted" - refute User.get_by_nickname(user.nickname) + assert %{deactivated: true} = User.get_by_nickname(user.nickname) end test "no user to delete" do diff --git a/test/user_test.exs b/test/user_test.exs index f3d044a80..555bbb92f 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -1127,16 +1127,7 @@ test ".delete_user_activities deletes all create activities", %{user: user} do refute Activity.get_by_id(activity.id) end - test "it deletes deactivated user" do - {:ok, user} = insert(:user, deactivated: true) |> User.set_cache() - - {:ok, job} = User.delete(user) - {:ok, _user} = ObanHelpers.perform(job) - - refute User.get_by_id(user.id) - end - - test "it deletes a user, all follow relationships and all activities", %{user: user} do + test "it deactivates a user, all follow relationships and all activities", %{user: user} do follower = insert(:user) {:ok, follower} = User.follow(follower, user) @@ -1156,8 +1147,7 @@ test "it deletes a user, all follow relationships and all activities", %{user: u follower = User.get_cached_by_id(follower.id) refute User.following?(follower, user) - refute User.get_by_id(user.id) - assert {:ok, nil} == Cachex.get(:user_cache, "ap_id:#{user.ap_id}") + assert %{deactivated: true} = User.get_by_id(user.id) user_activities = user.ap_id diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index efbca82f6..2baf9ce03 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -870,7 +870,8 @@ test "it fails for incoming deletes with spoofed origin" do @tag capture_log: true test "it works for incoming user deletes" do - %{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin") + %{ap_id: ap_id} = + insert(:user, ap_id: "http://mastodon.example.org/users/admin", local: false) data = File.read!("test/fixtures/mastodon-delete-user.json") -- cgit v1.2.3 From 78391a00c579696b1555bbe5373e128b932cfc3f Mon Sep 17 00:00:00 2001 From: eugenijm Date: Mon, 27 Apr 2020 17:41:38 +0300 Subject: Mastodon API: do not create a following relationship if the corresponding follow request doesn't exist when calling `POST /api/v1/follow_requests/:id/authorize` --- lib/pleroma/web/common_api/common_api.ex | 4 ++-- test/web/common_api/common_api_test.exs | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 091011c6b..511cae63f 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -39,8 +39,8 @@ def unfollow(follower, unfollowed) do end def accept_follow_request(follower, followed) do - with {:ok, follower} <- User.follow(follower, followed), - %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), + with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), + {:ok, follower} <- User.follow(follower, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept"), {:ok, _activity} <- diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index b80523160..9b7fce3a0 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -640,6 +640,14 @@ test "after rejection, it sets all existing pending follow request states to 're assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject" assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending" end + + test "doesn't create a following relationship if the corresponding follow request doesn't exist" do + user = insert(:user, locked: true) + not_follower = insert(:user) + CommonAPI.accept_follow_request(not_follower, user) + + assert Pleroma.FollowingRelationship.following?(not_follower, user) == false + end end describe "vote/3" do -- cgit v1.2.3 From 6e0b046771304bb8b733cebecc20a31a4df5e616 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 26 Apr 2020 00:28:57 -0500 Subject: Let blob: pass CSP --- docs/configuration/hardening.md | 2 +- lib/pleroma/plugs/http_security_plug.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration/hardening.md b/docs/configuration/hardening.md index b54c28850..d3bfc4e4a 100644 --- a/docs/configuration/hardening.md +++ b/docs/configuration/hardening.md @@ -36,7 +36,7 @@ content-security-policy: default-src 'none'; base-uri 'self'; frame-ancestors 'none'; - img-src 'self' data: https:; + img-src 'self' data: blob: https:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex index 81e6b4f2a..6462797b6 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/plugs/http_security_plug.ex @@ -75,7 +75,7 @@ defp csp_string do "default-src 'none'", "base-uri 'self'", "frame-ancestors 'none'", - "img-src 'self' data: https:", + "img-src 'self' data: blob: https:", "media-src 'self' https:", "style-src 'self' 'unsafe-inline'", "font-src 'self'", -- cgit v1.2.3 From 2e58fe08cd5035e66d34c87f70588f8a307d51ed Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 23 Apr 2020 13:33:30 +0200 Subject: CommonAPI: Don't make repeating announces possible --- lib/pleroma/web/common_api/common_api.ex | 27 +++++++++++++++------------ lib/pleroma/web/common_api/utils.ex | 18 ------------------ test/web/common_api/common_api_test.exs | 18 +++++++++++++++--- test/web/common_api/common_api_utils_test.exs | 20 -------------------- 4 files changed, 30 insertions(+), 53 deletions(-) diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 511cae63f..b9fa9fe3a 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -83,8 +83,9 @@ def delete(activity_id, user) do end end - def repeat(id_or_ap_id, user, params \\ %{}) do - with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)}, + def repeat(id, user, params \\ %{}) do + with {_, %Activity{data: %{"type" => "Create"}} = activity} <- + {:find_activity, Activity.get_by_id(id)}, object <- Object.normalize(activity), announce_activity <- Utils.get_existing_announce(user.ap_id, object), public <- public_announce?(object, params) do @@ -99,8 +100,9 @@ def repeat(id_or_ap_id, user, params \\ %{}) do end end - def unrepeat(id_or_ap_id, user) do - with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)} do + def unrepeat(id, user) do + with {_, %Activity{data: %{"type" => "Create"}} = activity} <- + {:find_activity, Activity.get_by_id(id)} do object = Object.normalize(activity) ActivityPub.unannounce(user, object) else @@ -109,8 +111,8 @@ def unrepeat(id_or_ap_id, user) do end end - def favorite(id_or_ap_id, user) do - with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)}, + def favorite(id, user) do + with {_, %Activity{} = activity} <- {:find_activity, Activity.get_by_id(id)}, object <- Object.normalize(activity), like_activity <- Utils.get_existing_like(user.ap_id, object) do if like_activity do @@ -124,8 +126,9 @@ def favorite(id_or_ap_id, user) do end end - def unfavorite(id_or_ap_id, user) do - with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)} do + def unfavorite(id, user) do + with {_, %Activity{data: %{"type" => "Create"}} = activity} <- + {:find_activity, Activity.get_by_id(id)} do object = Object.normalize(activity) ActivityPub.unlike(user, object) else @@ -316,12 +319,12 @@ def update(user) do }) end - def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do + def pin(id, %{ap_id: user_ap_id} = user) do with %Activity{ actor: ^user_ap_id, data: %{"type" => "Create"}, object: %Object{data: %{"type" => object_type}} - } = activity <- get_by_id_or_ap_id(id_or_ap_id), + } = activity <- Activity.get_by_id_with_object(id), true <- object_type in ["Note", "Article", "Question"], true <- Visibility.is_public?(activity), {:ok, _user} <- User.add_pinnned_activity(user, activity) do @@ -332,8 +335,8 @@ def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do end end - def unpin(id_or_ap_id, user) do - with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), + def unpin(id, user) do + with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id), {:ok, _user} <- User.remove_pinnned_activity(user, activity) do {:ok, activity} else diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 635e7cd38..26dcd463c 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -24,24 +24,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do require Logger require Pleroma.Constants - # This is a hack for twidere. - def get_by_id_or_ap_id(id) do - activity = - with true <- FlakeId.flake_id?(id), - %Activity{} = activity <- Activity.get_by_id_with_object(id) do - activity - else - _ -> Activity.get_create_by_object_ap_id_with_object(id) - end - - activity && - if activity.data["type"] == "Create" do - activity - else - Activity.get_create_by_object_ap_id_with_object(activity.data["object"]) - end - end - def attachments_from_ids(%{"media_ids" => ids, "descriptions" => desc} = _) do attachments_from_ids_descs(ids, desc) end diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 9b7fce3a0..9fe8fc0f3 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -268,6 +268,16 @@ test "repeating a status" do {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, user) end + test "can't repeat a repeat" do + user = insert(:user) + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) + + {:ok, %Activity{} = announce, _} = CommonAPI.repeat(activity.id, other_user) + + refute match?({:ok, %Activity{}, _}, CommonAPI.repeat(announce.id, user)) + end + test "repeating a status privately" do user = insert(:user) other_user = insert(:user) @@ -294,8 +304,8 @@ test "retweeting a status twice returns the status" do other_user = insert(:user) {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) - {:ok, %Activity{} = activity, object} = CommonAPI.repeat(activity.id, user) - {:ok, ^activity, ^object} = CommonAPI.repeat(activity.id, user) + {:ok, %Activity{} = announce, object} = CommonAPI.repeat(activity.id, user) + {:ok, ^announce, ^object} = CommonAPI.repeat(activity.id, user) end test "favoriting a status twice returns the status" do @@ -369,7 +379,9 @@ test "unpin status", %{user: user, activity: activity} do user = refresh_record(user) - assert {:ok, ^activity} = CommonAPI.unpin(activity.id, user) + id = activity.id + + assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user)) user = refresh_record(user) diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs index 45fc94522..5d24b5fc6 100644 --- a/test/web/common_api/common_api_utils_test.exs +++ b/test/web/common_api/common_api_utils_test.exs @@ -358,26 +358,6 @@ test "for direct posts, a reply" do end end - describe "get_by_id_or_ap_id/1" do - test "get activity by id" do - activity = insert(:note_activity) - %Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.id) - assert note.id == activity.id - end - - test "get activity by ap_id" do - activity = insert(:note_activity) - %Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.data["object"]) - assert note.id == activity.id - end - - test "get activity by object when type isn't `Create` " do - activity = insert(:like_activity) - %Pleroma.Activity{} = like = Utils.get_by_id_or_ap_id(activity.id) - assert like.data["object"] == activity.data["object"] - end - end - describe "to_master_date/1" do test "removes microseconds from date (NaiveDateTime)" do assert Utils.to_masto_date(~N[2015-01-23 23:50:07.123]) == "2015-01-23T23:50:07.000Z" -- cgit v1.2.3 From bf4b5f385631840a0ae25c9214a4e2d39413b1fc Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Wed, 22 Apr 2020 15:46:04 +0000 Subject: Merge branch 'fix/follow-and-blocks-import' into 'develop' Fix follower/blocks import when nicknames starts with @ Closes #1698 See merge request pleroma/pleroma!2416 --- .../web/twitter_api/controllers/util_controller.ex | 26 ++++++------ lib/pleroma/workers/background_worker.ex | 4 +- test/user_test.exs | 4 +- test/web/twitter_api/util_controller_test.exs | 47 ++++++++++++++++++++++ 4 files changed, 64 insertions(+), 17 deletions(-) diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index bca0e26eb..1873d78df 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -197,15 +197,16 @@ def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do end def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do - with lines <- String.split(list, "\n"), - followed_identifiers <- - Enum.map(lines, fn line -> - String.split(line, ",") |> List.first() - end) - |> List.delete("Account address") do - User.follow_import(follower, followed_identifiers) - json(conn, "job started") - end + followed_identifiers = + list + |> String.split("\n") + |> Enum.map(&(&1 |> String.split(",") |> List.first())) + |> List.delete("Account address") + |> Enum.map(&(&1 |> String.trim() |> String.trim_leading("@"))) + |> Enum.reject(&(&1 == "")) + + User.follow_import(follower, followed_identifiers) + json(conn, "job started") end def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do @@ -213,10 +214,9 @@ def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do end def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do - with blocked_identifiers <- String.split(list) do - User.blocks_import(blocker, blocked_identifiers) - json(conn, "job started") - end + blocked_identifiers = list |> String.split() |> Enum.map(&String.trim_leading(&1, "@")) + User.blocks_import(blocker, blocked_identifiers) + json(conn, "job started") end def change_password(%{assigns: %{user: user}} = conn, params) do diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex index 0f8ece2c4..57c3a9c3a 100644 --- a/lib/pleroma/workers/background_worker.ex +++ b/lib/pleroma/workers/background_worker.ex @@ -35,7 +35,7 @@ def perform( _job ) do blocker = User.get_cached_by_id(blocker_id) - User.perform(:blocks_import, blocker, blocked_identifiers) + {:ok, User.perform(:blocks_import, blocker, blocked_identifiers)} end def perform( @@ -47,7 +47,7 @@ def perform( _job ) do follower = User.get_cached_by_id(follower_id) - User.perform(:follow_import, follower, followed_identifiers) + {:ok, User.perform(:follow_import, follower, followed_identifiers)} end def perform(%{"op" => "media_proxy_preload", "message" => message}, _job) do diff --git a/test/user_test.exs b/test/user_test.exs index 555bbb92f..c48821a3c 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -760,8 +760,8 @@ test "it imports user followings from list" do ] {:ok, job} = User.follow_import(user1, identifiers) - result = ObanHelpers.perform(job) + assert {:ok, result} = ObanHelpers.perform(job) assert is_list(result) assert result == [user2, user3] end @@ -983,8 +983,8 @@ test "it imports user blocks from list" do ] {:ok, job} = User.blocks_import(user1, identifiers) - result = ObanHelpers.perform(job) + assert {:ok, result} = ObanHelpers.perform(job) assert is_list(result) assert result == [user2, user3] end diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index d464ce215..77c2d2892 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -94,6 +94,30 @@ test "requires 'follow' or 'write:follows' permissions" do end end end + + test "it imports follows with different nickname variations", %{conn: conn} do + [user2, user3, user4, user5, user6] = insert_list(5, :user) + + identifiers = + [ + user2.ap_id, + user3.nickname, + " ", + "@" <> user4.nickname, + user5.nickname <> "@localhost", + "@" <> user6.nickname <> "@localhost" + ] + |> Enum.join("\n") + + response = + conn + |> post("/api/pleroma/follow_import", %{"list" => identifiers}) + |> json_response(:ok) + + assert response == "job started" + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == [user2, user3, user4, user5, user6] + end end describe "POST /api/pleroma/blocks_import" do @@ -135,6 +159,29 @@ test "it imports blocks users from file", %{user: user1, conn: conn} do ) end end + + test "it imports blocks with different nickname variations", %{conn: conn} do + [user2, user3, user4, user5, user6] = insert_list(5, :user) + + identifiers = + [ + user2.ap_id, + user3.nickname, + "@" <> user4.nickname, + user5.nickname <> "@localhost", + "@" <> user6.nickname <> "@localhost" + ] + |> Enum.join(" ") + + response = + conn + |> post("/api/pleroma/blocks_import", %{"list" => identifiers}) + |> json_response(:ok) + + assert response == "job started" + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == [user2, user3, user4, user5, user6] + end end describe "PUT /api/pleroma/notification_settings" do -- cgit v1.2.3 From 2ff3b85326cdf42e5fc77c8b06836e0a6b97267f Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Wed, 22 Apr 2020 17:37:09 +0000 Subject: Merge branch 'bugfix/1670-user-count' into 'develop' Stats: Ignore internal users for user count. Closes #1670 See merge request pleroma/pleroma!2414 --- lib/pleroma/stats.ex | 18 ++++++++---- test/stat_test.exs | 70 --------------------------------------------- test/stats_test.exs | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 75 deletions(-) delete mode 100644 test/stat_test.exs create mode 100644 test/stats_test.exs diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex index 4446562ac..8d2809bbb 100644 --- a/lib/pleroma/stats.ex +++ b/lib/pleroma/stats.ex @@ -45,11 +45,11 @@ def get_peers do end def init(_args) do - {:ok, get_stat_data()} + {:ok, calculate_stat_data()} end def handle_call(:force_update, _from, _state) do - new_stats = get_stat_data() + new_stats = calculate_stat_data() {:reply, new_stats, new_stats} end @@ -58,12 +58,12 @@ def handle_call(:get_state, _from, state) do end def handle_cast(:run_update, _state) do - new_stats = get_stat_data() + new_stats = calculate_stat_data() {:noreply, new_stats} end - defp get_stat_data do + def calculate_stat_data do peers = from( u in User, @@ -77,7 +77,15 @@ defp get_stat_data do status_count = Repo.aggregate(User.Query.build(%{local: true}), :sum, :note_count) - user_count = Repo.aggregate(User.Query.build(%{local: true, active: true}), :count, :id) + users_query = + from(u in User, + where: u.deactivated != true, + where: u.local == true, + where: not is_nil(u.nickname), + where: not u.invisible + ) + + user_count = Repo.aggregate(users_query, :count, :id) %{ peers: peers, diff --git a/test/stat_test.exs b/test/stat_test.exs deleted file mode 100644 index 33b77e7e7..000000000 --- a/test/stat_test.exs +++ /dev/null @@ -1,70 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.StateTest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Web.CommonAPI - - describe "status visibility count" do - test "on new status" do - user = insert(:user) - other_user = insert(:user) - - CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) - - Enum.each(0..1, fn _ -> - CommonAPI.post(user, %{ - "visibility" => "unlisted", - "status" => "hey" - }) - end) - - Enum.each(0..2, fn _ -> - CommonAPI.post(user, %{ - "visibility" => "direct", - "status" => "hey @#{other_user.nickname}" - }) - end) - - Enum.each(0..3, fn _ -> - CommonAPI.post(user, %{ - "visibility" => "private", - "status" => "hey" - }) - end) - - assert %{direct: 3, private: 4, public: 1, unlisted: 2} = - Pleroma.Stats.get_status_visibility_count() - end - - test "on status delete" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) - assert %{public: 1} = Pleroma.Stats.get_status_visibility_count() - CommonAPI.delete(activity.id, user) - assert %{public: 0} = Pleroma.Stats.get_status_visibility_count() - end - - test "on status visibility update" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) - assert %{public: 1, private: 0} = Pleroma.Stats.get_status_visibility_count() - {:ok, _} = CommonAPI.update_activity_scope(activity.id, %{"visibility" => "private"}) - assert %{public: 0, private: 1} = Pleroma.Stats.get_status_visibility_count() - end - - test "doesn't count unrelated activities" do - user = insert(:user) - other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) - _ = CommonAPI.follow(user, other_user) - CommonAPI.favorite(activity.id, other_user) - CommonAPI.repeat(activity.id, other_user) - - assert %{direct: 0, private: 0, public: 1, unlisted: 0} = - Pleroma.Stats.get_status_visibility_count() - end - end -end diff --git a/test/stats_test.exs b/test/stats_test.exs new file mode 100644 index 000000000..8ddfb47a5 --- /dev/null +++ b/test/stats_test.exs @@ -0,0 +1,80 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.StatsTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.CommonAPI + + describe "user count" do + test "it ignores internal users" do + _user = insert(:user, local: true) + _internal = insert(:user, local: true, nickname: nil) + _internal = Pleroma.Web.ActivityPub.Relay.get_actor() + + assert match?(%{stats: %{user_count: 1}}, Pleroma.Stats.calculate_stat_data()) + end + end + + describe "status visibility count" do + test "on new status" do + user = insert(:user) + other_user = insert(:user) + + CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) + + Enum.each(0..1, fn _ -> + CommonAPI.post(user, %{ + "visibility" => "unlisted", + "status" => "hey" + }) + end) + + Enum.each(0..2, fn _ -> + CommonAPI.post(user, %{ + "visibility" => "direct", + "status" => "hey @#{other_user.nickname}" + }) + end) + + Enum.each(0..3, fn _ -> + CommonAPI.post(user, %{ + "visibility" => "private", + "status" => "hey" + }) + end) + + assert %{direct: 3, private: 4, public: 1, unlisted: 2} = + Pleroma.Stats.get_status_visibility_count() + end + + test "on status delete" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) + assert %{public: 1} = Pleroma.Stats.get_status_visibility_count() + CommonAPI.delete(activity.id, user) + assert %{public: 0} = Pleroma.Stats.get_status_visibility_count() + end + + test "on status visibility update" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) + assert %{public: 1, private: 0} = Pleroma.Stats.get_status_visibility_count() + {:ok, _} = CommonAPI.update_activity_scope(activity.id, %{"visibility" => "private"}) + assert %{public: 0, private: 1} = Pleroma.Stats.get_status_visibility_count() + end + + test "doesn't count unrelated activities" do + user = insert(:user) + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"}) + _ = CommonAPI.follow(user, other_user) + CommonAPI.favorite(activity.id, other_user) + CommonAPI.repeat(activity.id, other_user) + + assert %{direct: 0, private: 0, public: 1, unlisted: 0} = + Pleroma.Stats.get_status_visibility_count() + end + end +end -- cgit v1.2.3 From 0d74e38f5fcf4199a314cb4dff89bb1cfbc13995 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Sun, 19 Apr 2020 20:23:48 +0200 Subject: mix.exs: Do not bail out when .git doesn’t exists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mix.exs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/mix.exs b/mix.exs index 42bcee67d..571adc802 100644 --- a/mix.exs +++ b/mix.exs @@ -203,19 +203,26 @@ defp version(version) do identifier_filter = ~r/[^0-9a-z\-]+/i # Pre-release version, denoted from patch version with a hyphen + {tag, tag_err} = + System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true) + + {describe, describe_err} = System.cmd("git", ["describe", "--tags", "--abbrev=8"]) + {commit_hash, commit_hash_err} = System.cmd("git", ["rev-parse", "--short", "HEAD"]) + git_pre_release = - with {tag, 0} <- - System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true), - {describe, 0} <- System.cmd("git", ["describe", "--tags", "--abbrev=8"]) do - describe - |> String.trim() - |> String.replace(String.trim(tag), "") - |> String.trim_leading("-") - |> String.trim() - else - _ -> - {commit_hash, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"]) + cond do + tag_err == 0 and describe_err == 0 -> + describe + |> String.trim() + |> String.replace(String.trim(tag), "") + |> String.trim_leading("-") + |> String.trim() + + commit_hash_err == 0 -> "0-g" <> String.trim(commit_hash) + + true -> + "" end # Branch name as pre-release version component, denoted with a dot @@ -233,6 +240,8 @@ defp version(version) do |> String.replace(identifier_filter, "-") branch_name + else + _ -> "stable" end build_name = -- cgit v1.2.3 From 1ebf8db2a595c04da0e0ecbcd9c78b8833deecda Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 20 Apr 2020 12:59:16 +0000 Subject: Merge branch 'fix-object_age_policy' into 'develop' Fix ObjectAgePolicy See merge request pleroma/pleroma!2404 --- .../web/activity_pub/mrf/object_age_policy.ex | 10 ++++- .../activity_pub/mrf/object_age_policy_test.exs | 52 +++++++++++----------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex index 4a8bc91ae..b0ccb63c8 100644 --- a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex @@ -11,7 +11,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do @moduledoc "Filter activities depending on their age" @behaviour Pleroma.Web.ActivityPub.MRF - defp check_date(%{"published" => published} = message) do + defp check_date(%{"object" => %{"published" => published}} = message) do with %DateTime{} = now <- DateTime.utc_now(), {:ok, %DateTime{} = then, _} <- DateTime.from_iso8601(published), max_ttl <- Config.get([:mrf_object_age, :threshold]), @@ -96,5 +96,11 @@ def filter(%{"type" => "Create", "published" => _} = message) do def filter(message), do: {:ok, message} @impl true - def describe, do: {:ok, %{}} + def describe do + mrf_object_age = + Pleroma.Config.get(:mrf_object_age) + |> Enum.into(%{}) + + {:ok, %{mrf_object_age: mrf_object_age}} + end end diff --git a/test/web/activity_pub/mrf/object_age_policy_test.exs b/test/web/activity_pub/mrf/object_age_policy_test.exs index 643609da4..4815edd04 100644 --- a/test/web/activity_pub/mrf/object_age_policy_test.exs +++ b/test/web/activity_pub/mrf/object_age_policy_test.exs @@ -21,26 +21,38 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do :ok end + defp get_old_message do + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + end + + defp get_new_message do + old_message = get_old_message() + + new_object = + old_message + |> Map.get("object") + |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) + + old_message + |> Map.put("object", new_object) + end + describe "with reject action" do test "it rejects an old post" do Config.put([:mrf_object_age, :actions], [:reject]) - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() + data = get_old_message() - {:reject, _} = ObjectAgePolicy.filter(data) + assert match?({:reject, _}, ObjectAgePolicy.filter(data)) end test "it allows a new post" do Config.put([:mrf_object_age, :actions], [:reject]) - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) + data = get_new_message() - {:ok, _} = ObjectAgePolicy.filter(data) + assert match?({:ok, _}, ObjectAgePolicy.filter(data)) end end @@ -48,9 +60,7 @@ test "it allows a new post" do test "it delists an old post" do Config.put([:mrf_object_age, :actions], [:delist]) - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() + data = get_old_message() {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) @@ -62,14 +72,11 @@ test "it delists an old post" do test "it allows a new post" do Config.put([:mrf_object_age, :actions], [:delist]) - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) + data = get_new_message() {:ok, _user} = User.get_or_fetch_by_ap_id(data["actor"]) - {:ok, ^data} = ObjectAgePolicy.filter(data) + assert match?({:ok, ^data}, ObjectAgePolicy.filter(data)) end end @@ -77,9 +84,7 @@ test "it allows a new post" do test "it strips followers collections from an old post" do Config.put([:mrf_object_age, :actions], [:strip_followers]) - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() + data = get_old_message() {:ok, user} = User.get_or_fetch_by_ap_id(data["actor"]) @@ -92,14 +97,11 @@ test "it strips followers collections from an old post" do test "it allows a new post" do Config.put([:mrf_object_age, :actions], [:strip_followers]) - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) + data = get_new_message() {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) - {:ok, ^data} = ObjectAgePolicy.filter(data) + assert match?({:ok, ^data}, ObjectAgePolicy.filter(data)) end end end -- cgit v1.2.3 From da4923f2e59aac7f97812a756593602083f17626 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 16 Apr 2020 21:58:57 +0000 Subject: Merge branch 'authenticated-api-oauth-check-enforcement' into 'develop' Enforcement of OAuth scopes check for authenticated API endpoints See merge request pleroma/pleroma!2349 --- lib/pleroma/plugs/auth_expected_plug.ex | 17 ++++++++ lib/pleroma/plugs/oauth_scopes_plug.ex | 5 ++- lib/pleroma/plugs/plug_helper.ex | 38 +++++++++++++++++ lib/pleroma/tests/oauth_test_controller.ex | 31 ++++++++++++++ lib/pleroma/web/masto_fe_controller.ex | 2 +- .../mastodon_api/controllers/account_controller.ex | 9 +++- .../controllers/mastodon_api_controller.ex | 18 ++++++-- .../controllers/suggestion_controller.ex | 9 ++-- lib/pleroma/web/oauth/oauth_controller.ex | 2 + .../controllers/pleroma_api_controller.ex | 2 +- lib/pleroma/web/router.ex | 14 ++++++- .../web/twitter_api/twitter_api_controller.ex | 2 + lib/pleroma/web/web.ex | 49 ++++++++++++++++++++++ test/plugs/oauth_scopes_plug_test.exs | 13 ++++++ test/web/auth/oauth_test_controller_test.exs | 49 ++++++++++++++++++++++ .../controllers/suggestion_controller_test.exs | 26 ------------ .../controllers/pleroma_api_controller_test.exs | 2 +- 17 files changed, 248 insertions(+), 40 deletions(-) create mode 100644 lib/pleroma/plugs/auth_expected_plug.ex create mode 100644 lib/pleroma/plugs/plug_helper.ex create mode 100644 lib/pleroma/tests/oauth_test_controller.ex create mode 100644 test/web/auth/oauth_test_controller_test.exs diff --git a/lib/pleroma/plugs/auth_expected_plug.ex b/lib/pleroma/plugs/auth_expected_plug.ex new file mode 100644 index 000000000..f79597dc3 --- /dev/null +++ b/lib/pleroma/plugs/auth_expected_plug.ex @@ -0,0 +1,17 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Plugs.AuthExpectedPlug do + import Plug.Conn + + def init(options), do: options + + def call(conn, _) do + put_private(conn, :auth_expected, true) + end + + def auth_expected?(conn) do + conn.private[:auth_expected] + end +end diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex index 38df074ad..66f48c28c 100644 --- a/lib/pleroma/plugs/oauth_scopes_plug.ex +++ b/lib/pleroma/plugs/oauth_scopes_plug.ex @@ -8,12 +8,15 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do alias Pleroma.Config alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Plugs.PlugHelper + + use Pleroma.Web, :plug @behaviour Plug def init(%{scopes: _} = options), do: options - def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do + def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do op = options[:op] || :| token = assigns[:token] diff --git a/lib/pleroma/plugs/plug_helper.ex b/lib/pleroma/plugs/plug_helper.ex new file mode 100644 index 000000000..4f83e9414 --- /dev/null +++ b/lib/pleroma/plugs/plug_helper.ex @@ -0,0 +1,38 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Plugs.PlugHelper do + @moduledoc "Pleroma Plug helper" + + def append_to_called_plugs(conn, plug_module) do + append_to_private_list(conn, :called_plugs, plug_module) + end + + def append_to_skipped_plugs(conn, plug_module) do + append_to_private_list(conn, :skipped_plugs, plug_module) + end + + def plug_called?(conn, plug_module) do + contained_in_private_list?(conn, :called_plugs, plug_module) + end + + def plug_skipped?(conn, plug_module) do + contained_in_private_list?(conn, :skipped_plugs, plug_module) + end + + def plug_called_or_skipped?(conn, plug_module) do + plug_called?(conn, plug_module) || plug_skipped?(conn, plug_module) + end + + defp append_to_private_list(conn, private_variable, value) do + list = conn.private[private_variable] || [] + modified_list = Enum.uniq(list ++ [value]) + Plug.Conn.put_private(conn, private_variable, modified_list) + end + + defp contained_in_private_list?(conn, private_variable, value) do + list = conn.private[private_variable] || [] + value in list + end +end diff --git a/lib/pleroma/tests/oauth_test_controller.ex b/lib/pleroma/tests/oauth_test_controller.ex new file mode 100644 index 000000000..58d517f78 --- /dev/null +++ b/lib/pleroma/tests/oauth_test_controller.ex @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +# A test controller reachable only in :test env. +# Serves to test OAuth scopes check skipping / enforcement. +defmodule Pleroma.Tests.OAuthTestController do + @moduledoc false + + use Pleroma.Web, :controller + + alias Pleroma.Plugs.OAuthScopesPlug + + plug(:skip_plug, OAuthScopesPlug when action == :skipped_oauth) + + plug(OAuthScopesPlug, %{scopes: ["read"]} when action != :missed_oauth) + + def skipped_oauth(conn, _params) do + noop(conn) + end + + def performed_oauth(conn, _params) do + noop(conn) + end + + def missed_oauth(conn, _params) do + noop(conn) + end + + defp noop(conn), do: json(conn, %{}) +end diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex index 43649ad26..557cde328 100644 --- a/lib/pleroma/web/masto_fe_controller.ex +++ b/lib/pleroma/web/masto_fe_controller.ex @@ -17,7 +17,7 @@ defmodule Pleroma.Web.MastoFEController do when action == :index ) - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index) + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action not in [:index, :manifest]) @doc "GET /web/*path" def index(%{assigns: %{user: user, token: token}} = conn, _params) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 73853c1e4..229d4be28 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -15,10 +15,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.ListView alias Pleroma.Web.MastodonAPI.MastodonAPI + alias Pleroma.Web.MastodonAPI.MastodonAPIController alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.OAuth.Token alias Pleroma.Web.TwitterAPI.TwitterAPI + plug(:skip_plug, OAuthScopesPlug when action == :identity_proofs) + plug( OAuthScopesPlug, %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]} @@ -366,6 +369,8 @@ def blocks(%{assigns: %{user: user}} = conn, _) do end @doc "GET /api/v1/endorsements" - def endorsements(conn, params), - do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) + def endorsements(conn, params), do: MastodonAPIController.empty_array(conn, params) + + @doc "GET /api/v1/identity_proofs" + def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params) end diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index 14075307d..ac8c18f24 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -3,21 +3,31 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do + @moduledoc """ + Contains stubs for unimplemented Mastodon API endpoints. + + Note: instead of routing directly to this controller's action, + it's preferable to define an action in relevant (non-generic) controller, + set up OAuth rules for it and call this controller's function from it. + """ + use Pleroma.Web, :controller require Logger + plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug when action in [:empty_array, :empty_object]) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) - # Stubs for unimplemented mastodon api - # def empty_array(conn, _) do - Logger.debug("Unimplemented, returning an empty array") + Logger.debug("Unimplemented, returning an empty array (list)") json(conn, []) end def empty_object(conn, _) do - Logger.debug("Unimplemented, returning an empty object") + Logger.debug("Unimplemented, returning an empty object (map)") json(conn, %{}) end end diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index 0cdc7bd8d..c93a43969 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -5,10 +5,13 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug + require Logger + plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :index) + @doc "GET /api/v1/suggestions" - def index(conn, _) do - json(conn, []) - end + def index(conn, params), + do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) end diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 46688db7e..0121cd661 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -27,6 +27,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do plug(:fetch_flash) plug(RateLimiter, [name: :authentication] when action == :create_authorization) + plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug) + action_fallback(Pleroma.Web.OAuth.FallbackController) @oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob" diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index 41677d04d..f0867c2c1 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -34,7 +34,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do plug( OAuthScopesPlug, - %{scopes: ["write:conversations"]} when action == :update_conversation + %{scopes: ["write:conversations"]} when action in [:update_conversation, :read_conversations] ) plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :read_notification) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index cb590acfb..bc2cf8b44 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -34,6 +34,7 @@ defmodule Pleroma.Web.Router do pipeline :authenticated_api do plug(:accepts, ["json"]) plug(:fetch_session) + plug(Pleroma.Plugs.AuthExpectedPlug) plug(Pleroma.Plugs.OAuthPlug) plug(Pleroma.Plugs.BasicAuthDecoderPlug) plug(Pleroma.Plugs.UserFetcherPlug) @@ -334,7 +335,7 @@ defmodule Pleroma.Web.Router do get("/accounts/relationships", AccountController, :relationships) get("/accounts/:id/lists", AccountController, :lists) - get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) + get("/accounts/:id/identity_proofs", AccountController, :identity_proofs) get("/follow_requests", FollowRequestController, :index) get("/blocks", AccountController, :blocks) @@ -657,6 +658,17 @@ defmodule Pleroma.Web.Router do end end + # Test-only routes needed to test action dispatching and plug chain execution + if Pleroma.Config.get(:env) == :test do + scope "/test/authenticated_api", Pleroma.Tests do + pipe_through(:authenticated_api) + + for action <- [:skipped_oauth, :performed_oauth, :missed_oauth] do + get("/#{action}", OAuthTestController, action) + end + end + end + scope "/", Pleroma.Web.MongooseIM do get("/user_exists", MongooseIMController, :user_exists) get("/check_password", MongooseIMController, :check_password) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 0229aea97..31adc2817 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -15,6 +15,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read) + plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token]) + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) action_fallback(:errors) diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex index cf3ac1287..ae7c94640 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web/web.ex @@ -29,11 +29,40 @@ def controller do import Pleroma.Web.Router.Helpers import Pleroma.Web.TranslationHelpers + alias Pleroma.Plugs.PlugHelper + plug(:set_put_layout) defp set_put_layout(conn, _) do put_layout(conn, Pleroma.Config.get(:app_layout, "app.html")) end + + # Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain + defp skip_plug(conn, plug_module) do + try do + plug_module.ensure_skippable() + rescue + UndefinedFunctionError -> + raise "#{plug_module} is not skippable. Append `use Pleroma.Web, :plug` to its code." + end + + PlugHelper.append_to_skipped_plugs(conn, plug_module) + end + + # Here we can apply before-action hooks (e.g. verify whether auth checks were preformed) + defp action(conn, params) do + if Pleroma.Plugs.AuthExpectedPlug.auth_expected?(conn) && + not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do + conn + |> render_error( + :forbidden, + "Security violation: OAuth scopes check was neither handled nor explicitly skipped." + ) + |> halt() + else + super(conn, params) + end + end end end @@ -96,6 +125,26 @@ def channel do end end + def plug do + quote do + alias Pleroma.Plugs.PlugHelper + + def ensure_skippable, do: :noop + + @impl Plug + @doc "If marked as skipped, returns `conn`, and calls `perform/2` otherwise." + def call(%Plug.Conn{} = conn, options) do + if PlugHelper.plug_skipped?(conn, __MODULE__) do + conn + else + conn + |> PlugHelper.append_to_called_plugs(__MODULE__) + |> perform(options) + end + end + end + end + @doc """ When used, dispatch to the appropriate controller/view/etc. """ diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/plugs/oauth_scopes_plug_test.exs index 1b3aa85b6..85105f968 100644 --- a/test/plugs/oauth_scopes_plug_test.exs +++ b/test/plugs/oauth_scopes_plug_test.exs @@ -7,6 +7,7 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Plugs.PlugHelper alias Pleroma.Repo import Mock @@ -16,6 +17,18 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do :ok end + test "is not performed if marked as skipped", %{conn: conn} do + with_mock OAuthScopesPlug, [:passthrough], perform: &passthrough([&1, &2]) do + conn = + conn + |> PlugHelper.append_to_skipped_plugs(OAuthScopesPlug) + |> OAuthScopesPlug.call(%{scopes: ["random_scope"]}) + + refute called(OAuthScopesPlug.perform(:_, :_)) + refute conn.halted + end + end + test "if `token.scopes` fulfills specified 'any of' conditions, " <> "proceeds with no op", %{conn: conn} do diff --git a/test/web/auth/oauth_test_controller_test.exs b/test/web/auth/oauth_test_controller_test.exs new file mode 100644 index 000000000..a2f6009ac --- /dev/null +++ b/test/web/auth/oauth_test_controller_test.exs @@ -0,0 +1,49 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Tests.OAuthTestControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + setup %{conn: conn} do + user = insert(:user) + conn = assign(conn, :user, user) + %{conn: conn, user: user} + end + + test "missed_oauth", %{conn: conn} do + res = + conn + |> get("/test/authenticated_api/missed_oauth") + |> json_response(403) + + assert res == + %{ + "error" => + "Security violation: OAuth scopes check was neither handled nor explicitly skipped." + } + end + + test "skipped_oauth", %{conn: conn} do + conn + |> assign(:token, nil) + |> get("/test/authenticated_api/skipped_oauth") + |> json_response(200) + end + + test "performed_oauth", %{user: user} do + %{conn: good_token_conn} = oauth_access(["read"], user: user) + + good_token_conn + |> get("/test/authenticated_api/performed_oauth") + |> json_response(200) + + %{conn: bad_token_conn} = oauth_access(["follow"], user: user) + + bad_token_conn + |> get("/test/authenticated_api/performed_oauth") + |> json_response(403) + end +end diff --git a/test/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/web/mastodon_api/controllers/suggestion_controller_test.exs index c697a39f8..8d0e70db8 100644 --- a/test/web/mastodon_api/controllers/suggestion_controller_test.exs +++ b/test/web/mastodon_api/controllers/suggestion_controller_test.exs @@ -7,34 +7,8 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do alias Pleroma.Config - import Pleroma.Factory - import Tesla.Mock - setup do: oauth_access(["read"]) - setup %{user: user} do - other_user = insert(:user) - host = Config.get([Pleroma.Web.Endpoint, :url, :host]) - url500 = "http://test500?#{host}&#{user.nickname}" - url200 = "http://test200?#{host}&#{user.nickname}" - - mock(fn - %{method: :get, url: ^url500} -> - %Tesla.Env{status: 500, body: "bad request"} - - %{method: :get, url: ^url200} -> - %Tesla.Env{ - status: 200, - body: - ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{ - other_user.ap_id - }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}]) - } - end) - - [other_user: other_user] - end - test "returns empty result", %{conn: conn} do res = conn diff --git a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs index 32250f06f..8f0cbe9b2 100644 --- a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs +++ b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs @@ -203,7 +203,7 @@ test "PATCH /api/v1/pleroma/conversations/:id" do test "POST /api/v1/pleroma/conversations/read" do user = insert(:user) - %{user: other_user, conn: conn} = oauth_access(["write:notifications"]) + %{user: other_user, conn: conn} = oauth_access(["write:conversations"]) {:ok, _activity} = CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}", "visibility" => "direct"}) -- cgit v1.2.3 From 862d4886c9c600ff0ff85edc744e3c05a3fcd68d Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Fri, 17 Apr 2020 21:21:10 +0300 Subject: [#1682] Fixed Basic Auth permissions issue by disabling OAuth scopes checks when password is provided. Refactored plugs skipping functionality. --- lib/pleroma/plugs/authentication_plug.ex | 6 +++- lib/pleroma/plugs/legacy_authentication_plug.ex | 3 ++ lib/pleroma/plugs/plug_helper.ex | 24 +++++++------ lib/pleroma/web/web.ex | 28 +++++++++++---- test/plugs/authentication_plug_test.exs | 7 +++- test/plugs/legacy_authentication_plug_test.exs | 6 +++- test/plugs/oauth_scopes_plug_test.exs | 3 +- test/web/auth/basic_auth_test.exs | 46 +++++++++++++++++++++++++ 8 files changed, 100 insertions(+), 23 deletions(-) create mode 100644 test/web/auth/basic_auth_test.exs diff --git a/lib/pleroma/plugs/authentication_plug.ex b/lib/pleroma/plugs/authentication_plug.ex index 089028d77..0061c69dc 100644 --- a/lib/pleroma/plugs/authentication_plug.ex +++ b/lib/pleroma/plugs/authentication_plug.ex @@ -4,8 +4,11 @@ defmodule Pleroma.Plugs.AuthenticationPlug do alias Comeonin.Pbkdf2 - import Plug.Conn + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User + + import Plug.Conn + require Logger def init(options), do: options @@ -37,6 +40,7 @@ def call( if Pbkdf2.checkpw(password, password_hash) do conn |> assign(:user, auth_user) + |> OAuthScopesPlug.skip_plug() else conn end diff --git a/lib/pleroma/plugs/legacy_authentication_plug.ex b/lib/pleroma/plugs/legacy_authentication_plug.ex index 5c5c36c56..d346e01a6 100644 --- a/lib/pleroma/plugs/legacy_authentication_plug.ex +++ b/lib/pleroma/plugs/legacy_authentication_plug.ex @@ -4,6 +4,8 @@ defmodule Pleroma.Plugs.LegacyAuthenticationPlug do import Plug.Conn + + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User def init(options) do @@ -27,6 +29,7 @@ def call( conn |> assign(:auth_user, user) |> assign(:user, user) + |> OAuthScopesPlug.skip_plug() else _ -> conn diff --git a/lib/pleroma/plugs/plug_helper.ex b/lib/pleroma/plugs/plug_helper.ex index 4f83e9414..9c67be8ef 100644 --- a/lib/pleroma/plugs/plug_helper.ex +++ b/lib/pleroma/plugs/plug_helper.ex @@ -5,30 +5,32 @@ defmodule Pleroma.Plugs.PlugHelper do @moduledoc "Pleroma Plug helper" - def append_to_called_plugs(conn, plug_module) do - append_to_private_list(conn, :called_plugs, plug_module) - end + @called_plugs_list_id :called_plugs + def called_plugs_list_id, do: @called_plugs_list_id - def append_to_skipped_plugs(conn, plug_module) do - append_to_private_list(conn, :skipped_plugs, plug_module) - end + @skipped_plugs_list_id :skipped_plugs + def skipped_plugs_list_id, do: @skipped_plugs_list_id + @doc "Returns `true` if specified plug was called." def plug_called?(conn, plug_module) do - contained_in_private_list?(conn, :called_plugs, plug_module) + contained_in_private_list?(conn, @called_plugs_list_id, plug_module) end + @doc "Returns `true` if specified plug was explicitly marked as skipped." def plug_skipped?(conn, plug_module) do - contained_in_private_list?(conn, :skipped_plugs, plug_module) + contained_in_private_list?(conn, @skipped_plugs_list_id, plug_module) end + @doc "Returns `true` if specified plug was either called or explicitly marked as skipped." def plug_called_or_skipped?(conn, plug_module) do plug_called?(conn, plug_module) || plug_skipped?(conn, plug_module) end - defp append_to_private_list(conn, private_variable, value) do - list = conn.private[private_variable] || [] + # Appends plug to known list (skipped, called). Intended to be used from within plug code only. + def append_to_private_list(conn, list_id, value) do + list = conn.private[list_id] || [] modified_list = Enum.uniq(list ++ [value]) - Plug.Conn.put_private(conn, private_variable, modified_list) + Plug.Conn.put_private(conn, list_id, modified_list) end defp contained_in_private_list?(conn, private_variable, value) do diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex index ae7c94640..bf48ce26c 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web/web.ex @@ -40,17 +40,22 @@ defp set_put_layout(conn, _) do # Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain defp skip_plug(conn, plug_module) do try do - plug_module.ensure_skippable() + plug_module.skip_plug(conn) rescue UndefinedFunctionError -> raise "#{plug_module} is not skippable. Append `use Pleroma.Web, :plug` to its code." end - - PlugHelper.append_to_skipped_plugs(conn, plug_module) end - # Here we can apply before-action hooks (e.g. verify whether auth checks were preformed) + # Executed just before actual controller action, invokes before-action hooks (callbacks) defp action(conn, params) do + with %Plug.Conn{halted: false} <- maybe_halt_on_missing_oauth_scopes_check(conn) do + super(conn, params) + end + end + + # Halts if authenticated API action neither performs nor explicitly skips OAuth scopes check + defp maybe_halt_on_missing_oauth_scopes_check(conn) do if Pleroma.Plugs.AuthExpectedPlug.auth_expected?(conn) && not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do conn @@ -60,7 +65,7 @@ defp action(conn, params) do ) |> halt() else - super(conn, params) + conn end end end @@ -129,7 +134,16 @@ def plug do quote do alias Pleroma.Plugs.PlugHelper - def ensure_skippable, do: :noop + @doc """ + Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain. + """ + def skip_plug(conn) do + PlugHelper.append_to_private_list( + conn, + PlugHelper.skipped_plugs_list_id(), + __MODULE__ + ) + end @impl Plug @doc "If marked as skipped, returns `conn`, and calls `perform/2` otherwise." @@ -138,7 +152,7 @@ def call(%Plug.Conn{} = conn, options) do conn else conn - |> PlugHelper.append_to_called_plugs(__MODULE__) + |> PlugHelper.append_to_private_list(PlugHelper.called_plugs_list_id(), __MODULE__) |> perform(options) end end diff --git a/test/plugs/authentication_plug_test.exs b/test/plugs/authentication_plug_test.exs index ae2f3f8ec..646bda9d3 100644 --- a/test/plugs/authentication_plug_test.exs +++ b/test/plugs/authentication_plug_test.exs @@ -6,6 +6,8 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do use Pleroma.Web.ConnCase, async: true alias Pleroma.Plugs.AuthenticationPlug + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Plugs.PlugHelper alias Pleroma.User import ExUnit.CaptureLog @@ -36,13 +38,16 @@ test "it does nothing if a user is assigned", %{conn: conn} do assert ret_conn == conn end - test "with a correct password in the credentials, it assigns the auth_user", %{conn: conn} do + test "with a correct password in the credentials, " <> + "it assigns the auth_user and marks OAuthScopesPlug as skipped", + %{conn: conn} do conn = conn |> assign(:auth_credentials, %{password: "guy"}) |> AuthenticationPlug.call(%{}) assert conn.assigns.user == conn.assigns.auth_user + assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) end test "with a wrong password in the credentials, it does nothing", %{conn: conn} do diff --git a/test/plugs/legacy_authentication_plug_test.exs b/test/plugs/legacy_authentication_plug_test.exs index 7559de7d3..3b8c07627 100644 --- a/test/plugs/legacy_authentication_plug_test.exs +++ b/test/plugs/legacy_authentication_plug_test.exs @@ -8,6 +8,8 @@ defmodule Pleroma.Plugs.LegacyAuthenticationPlugTest do import Pleroma.Factory alias Pleroma.Plugs.LegacyAuthenticationPlug + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Plugs.PlugHelper alias Pleroma.User setup do @@ -36,7 +38,8 @@ test "it does nothing if a user is assigned", %{conn: conn, user: user} do end @tag :skip_on_mac - test "it authenticates the auth_user if present and password is correct and resets the password", + test "if `auth_user` is present and password is correct, " <> + "it authenticates the user, resets the password, marks OAuthScopesPlug as skipped", %{ conn: conn, user: user @@ -49,6 +52,7 @@ test "it authenticates the auth_user if present and password is correct and rese conn = LegacyAuthenticationPlug.call(conn, %{}) assert conn.assigns.user.id == user.id + assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) end @tag :skip_on_mac diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/plugs/oauth_scopes_plug_test.exs index 85105f968..d855d4f54 100644 --- a/test/plugs/oauth_scopes_plug_test.exs +++ b/test/plugs/oauth_scopes_plug_test.exs @@ -7,7 +7,6 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.PlugHelper alias Pleroma.Repo import Mock @@ -21,7 +20,7 @@ test "is not performed if marked as skipped", %{conn: conn} do with_mock OAuthScopesPlug, [:passthrough], perform: &passthrough([&1, &2]) do conn = conn - |> PlugHelper.append_to_skipped_plugs(OAuthScopesPlug) + |> OAuthScopesPlug.skip_plug() |> OAuthScopesPlug.call(%{scopes: ["random_scope"]}) refute called(OAuthScopesPlug.perform(:_, :_)) diff --git a/test/web/auth/basic_auth_test.exs b/test/web/auth/basic_auth_test.exs new file mode 100644 index 000000000..64f8a6863 --- /dev/null +++ b/test/web/auth/basic_auth_test.exs @@ -0,0 +1,46 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Auth.BasicAuthTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + test "with HTTP Basic Auth used, grants access to OAuth scope-restricted endpoints", %{ + conn: conn + } do + user = insert(:user) + assert Comeonin.Pbkdf2.checkpw("test", user.password_hash) + + basic_auth_contents = + (URI.encode_www_form(user.nickname) <> ":" <> URI.encode_www_form("test")) + |> Base.encode64() + + # Succeeds with HTTP Basic Auth + response = + conn + |> put_req_header("authorization", "Basic " <> basic_auth_contents) + |> get("/api/v1/accounts/verify_credentials") + |> json_response(200) + + user_nickname = user.nickname + assert %{"username" => ^user_nickname} = response + + # Succeeds with a properly scoped OAuth token + valid_token = insert(:oauth_token, scopes: ["read:accounts"]) + + conn + |> put_req_header("authorization", "Bearer #{valid_token.token}") + |> get("/api/v1/accounts/verify_credentials") + |> json_response(200) + + # Fails with a wrong-scoped OAuth token (proof of restriction) + invalid_token = insert(:oauth_token, scopes: ["read:something"]) + + conn + |> put_req_header("authorization", "Bearer #{invalid_token.token}") + |> get("/api/v1/accounts/verify_credentials") + |> json_response(403) + end +end -- cgit v1.2.3 From 8cf4e1619e439b1c9374a52cfc2b0cdf8d549d02 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 14 Apr 2020 11:43:53 -0500 Subject: Fix Oban not receiving :ok from RichMediaHelper job --- lib/pleroma/web/rich_media/helpers.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index 0314535d2..9d3d7f978 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -64,5 +64,8 @@ def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) d def fetch_data_for_activity(_), do: %{} - def perform(:fetch, %Activity{} = activity), do: fetch_data_for_activity(activity) + def perform(:fetch, %Activity{} = activity) do + fetch_data_for_activity(activity) + :ok + end end -- cgit v1.2.3 From a4afeed4266e75279422a6721f0a9a2aece0b9ea Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 15 Apr 2020 12:05:22 +0200 Subject: Uploads: Sandbox them in the CSP. --- lib/pleroma/plugs/uploaded_media.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pleroma/plugs/uploaded_media.ex b/lib/pleroma/plugs/uploaded_media.ex index 36ff024a7..94147e0c4 100644 --- a/lib/pleroma/plugs/uploaded_media.ex +++ b/lib/pleroma/plugs/uploaded_media.ex @@ -41,6 +41,7 @@ def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do conn -> conn end + |> merge_resp_headers([{"content-security-policy", "sandbox"}]) config = Pleroma.Config.get(Pleroma.Upload) -- cgit v1.2.3 From fb9ec885cc96e51bd7703d98d8e41305ead56b9e Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Fri, 17 Apr 2020 08:55:46 +0000 Subject: Merge branch 'feature/1677-need_reboot-flag-endpoint' into 'develop' Added need_reboot endpoint to admin api Closes #1677 See merge request pleroma/pleroma!2373 --- docs/API/admin_api.md | 21 +++++++++++--- lib/pleroma/web/admin_api/admin_api_controller.ex | 35 ++++++++--------------- lib/pleroma/web/router.ex | 1 + test/web/admin_api/admin_api_controller_test.exs | 18 ++++++++++-- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/docs/API/admin_api.md b/docs/API/admin_api.md index edcf73e14..26f3b1932 100644 --- a/docs/API/admin_api.md +++ b/docs/API/admin_api.md @@ -773,6 +773,8 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret ### Restarts pleroma application +**Only works when configuration from database is enabled.** + - Params: none - Response: - On failure: @@ -782,11 +784,24 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret {} ``` +## `GET /api/pleroma/admin/need_reboot` + +### Returns the flag whether the pleroma should be restarted + +- Params: none +- Response: + - `need_reboot` - boolean +```json +{ + "need_reboot": false +} +``` + ## `GET /api/pleroma/admin/config` ### Get list of merged default settings with saved in database. -*If `need_reboot` flag exists in response, instance must be restarted, so reboot time settings can take effect.* +*If `need_reboot` is `true`, instance must be restarted, so reboot time settings can take effect.* **Only works when configuration from database is enabled.** @@ -808,13 +823,12 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret "need_reboot": true } ``` - need_reboot - *optional*, if were changed reboot time settings. ## `POST /api/pleroma/admin/config` ### Update config settings -*If `need_reboot` flag exists in response, instance must be restarted, so reboot time settings can take effect.* +*If `need_reboot` is `true`, instance must be restarted, so reboot time settings can take effect.* **Only works when configuration from database is enabled.** @@ -956,7 +970,6 @@ config :quack, "need_reboot": true } ``` -need_reboot - *optional*, if were changed reboot time settings. ## ` GET /api/pleroma/admin/config/descriptions` diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 6c88549f5..e1869678e 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -911,16 +911,7 @@ def config_show(conn, _params) do end) |> List.flatten() - response = %{configs: merged} - - response = - if Restarter.Pleroma.need_reboot?() do - Map.put(response, :need_reboot, true) - else - response - end - - json(conn, response) + json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()}) end end @@ -947,28 +938,22 @@ def config_update(conn, %{"configs" => configs}) do Config.TransferTask.load_and_update_env(deleted, false) - need_reboot? = - Restarter.Pleroma.need_reboot?() || - Enum.any?(updated, fn config -> + if !Restarter.Pleroma.need_reboot?() do + changed_reboot_settings? = + (updated ++ deleted) + |> Enum.any?(fn config -> group = ConfigDB.from_string(config.group) key = ConfigDB.from_string(config.key) value = ConfigDB.from_binary(config.value) Config.TransferTask.pleroma_need_restart?(group, key, value) end) - response = %{configs: updated} - - response = - if need_reboot? do - Restarter.Pleroma.need_reboot() - Map.put(response, :need_reboot, need_reboot?) - else - response - end + if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot() + end conn |> put_view(ConfigView) - |> render("index.json", response) + |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()}) end end @@ -980,6 +965,10 @@ def restart(conn, _params) do end end + def need_reboot(conn, _params) do + json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()}) + end + defp configurable_from_database(conn) do if Config.get(:configurable_from_database) do :ok diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index bc2cf8b44..1da9478db 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -200,6 +200,7 @@ defmodule Pleroma.Web.Router do get("/config", AdminAPIController, :config_show) post("/config", AdminAPIController, :config_update) get("/config/descriptions", AdminAPIController, :config_descriptions) + get("/need_reboot", AdminAPIController, :need_reboot) get("/restart", AdminAPIController, :restart) get("/moderation_log", AdminAPIController, :list_log) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 6f5a4d059..64ed8ebec 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -2291,7 +2291,7 @@ test "saving config which need pleroma reboot", %{conn: conn} do |> get("/api/pleroma/admin/config") |> json_response(200) - refute Map.has_key?(configs, "need_reboot") + assert configs["need_reboot"] == false end test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do @@ -2347,7 +2347,7 @@ test "update setting which need reboot, don't change reboot flag until reboot", |> get("/api/pleroma/admin/config") |> json_response(200) - refute Map.has_key?(configs, "need_reboot") + assert configs["need_reboot"] == false end test "saving config with nested merge", %{conn: conn} do @@ -3065,6 +3065,20 @@ test "pleroma restarts", %{conn: conn} do end end + test "need_reboot flag", %{conn: conn} do + assert conn + |> get("/api/pleroma/admin/need_reboot") + |> json_response(200) == %{"need_reboot" => false} + + Restarter.Pleroma.need_reboot() + + assert conn + |> get("/api/pleroma/admin/need_reboot") + |> json_response(200) == %{"need_reboot" => true} + + on_exit(fn -> Restarter.Pleroma.refresh() end) + end + describe "GET /api/pleroma/admin/statuses" do test "returns all public, unlisted, and direct statuses", %{conn: conn, admin: admin} do blocked = insert(:user) -- cgit v1.2.3 From 138e6ece1b4185fbe8d8a8f7873ae0c3ee0837e6 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 11 Apr 2020 19:46:04 +0000 Subject: Merge branch 'following-relationships-optimizations' into 'develop' FollowingRelationship storage & performance optimizations See merge request pleroma/pleroma!2332 --- lib/pleroma/ecto_enums.ex | 8 +- lib/pleroma/following_relationship.ex | 79 +++++++++++++++-- lib/pleroma/user.ex | 36 ++++++-- lib/pleroma/user/query.ex | 6 +- lib/pleroma/user_relationship.ex | 99 ++++++++++++++++++++-- lib/pleroma/web/activity_pub/transmogrifier.ex | 13 +-- lib/pleroma/web/common_api/common_api.ex | 4 +- ...ge_following_relationships_state_to_integer.exs | 29 +++++++ ..._following_relationships_following_id_index.exs | 11 +++ test/following_relationship_test.exs | 8 +- test/tasks/user_test.exs | 2 +- test/user_test.exs | 9 +- test/web/activity_pub/transmogrifier_test.exs | 2 +- test/web/common_api/common_api_test.exs | 4 +- .../controllers/follow_request_controller_test.exs | 4 +- test/web/streamer/streamer_test.exs | 6 +- 16 files changed, 269 insertions(+), 51 deletions(-) create mode 100644 priv/repo/migrations/20200328124805_change_following_relationships_state_to_integer.exs create mode 100644 priv/repo/migrations/20200328130139_add_following_relationships_following_id_index.exs diff --git a/lib/pleroma/ecto_enums.ex b/lib/pleroma/ecto_enums.ex index d9b601223..6fc47620c 100644 --- a/lib/pleroma/ecto_enums.ex +++ b/lib/pleroma/ecto_enums.ex @@ -4,10 +4,16 @@ import EctoEnum -defenum(UserRelationshipTypeEnum, +defenum(Pleroma.UserRelationship.Type, block: 1, mute: 2, reblog_mute: 3, notification_mute: 4, inverse_subscription: 5 ) + +defenum(Pleroma.FollowingRelationship.State, + follow_pending: 1, + follow_accept: 2, + follow_reject: 3 +) diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index a6d281151..9ccf40495 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -8,12 +8,13 @@ defmodule Pleroma.FollowingRelationship do import Ecto.Changeset import Ecto.Query + alias Ecto.Changeset alias FlakeId.Ecto.CompatType alias Pleroma.Repo alias Pleroma.User schema "following_relationships" do - field(:state, :string, default: "accept") + field(:state, Pleroma.FollowingRelationship.State, default: :follow_pending) belongs_to(:follower, User, type: CompatType) belongs_to(:following, User, type: CompatType) @@ -27,6 +28,18 @@ def changeset(%__MODULE__{} = following_relationship, attrs) do |> put_assoc(:follower, attrs.follower) |> put_assoc(:following, attrs.following) |> validate_required([:state, :follower, :following]) + |> unique_constraint(:follower_id, + name: :following_relationships_follower_id_following_id_index + ) + |> validate_not_self_relationship() + end + + def state_to_enum(state) when state in ["pending", "accept", "reject"] do + String.to_existing_atom("follow_#{state}") + end + + def state_to_enum(state) do + raise "State is not convertible to Pleroma.FollowingRelationship.State: #{state}" end def get(%User{} = follower, %User{} = following) do @@ -35,7 +48,7 @@ def get(%User{} = follower, %User{} = following) do |> Repo.one() end - def update(follower, following, "reject"), do: unfollow(follower, following) + def update(follower, following, :follow_reject), do: unfollow(follower, following) def update(%User{} = follower, %User{} = following, state) do case get(follower, following) do @@ -50,7 +63,7 @@ def update(%User{} = follower, %User{} = following, state) do end end - def follow(%User{} = follower, %User{} = following, state \\ "accept") do + def follow(%User{} = follower, %User{} = following, state \\ :follow_accept) do %__MODULE__{} |> changeset(%{follower: follower, following: following, state: state}) |> Repo.insert(on_conflict: :nothing) @@ -80,7 +93,7 @@ def following_count(%User{} = user) do def get_follow_requests(%User{id: id}) do __MODULE__ |> join(:inner, [r], f in assoc(r, :follower)) - |> where([r], r.state == "pending") + |> where([r], r.state == ^:follow_pending) |> where([r], r.following_id == ^id) |> select([r, f], f) |> Repo.all() @@ -88,7 +101,7 @@ def get_follow_requests(%User{id: id}) do def following?(%User{id: follower_id}, %User{id: followed_id}) do __MODULE__ - |> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept") + |> where(follower_id: ^follower_id, following_id: ^followed_id, state: ^:follow_accept) |> Repo.exists?() end @@ -97,7 +110,7 @@ def following(%User{} = user) do __MODULE__ |> join(:inner, [r], u in User, on: r.following_id == u.id) |> where([r], r.follower_id == ^user.id) - |> where([r], r.state == "accept") + |> where([r], r.state == ^:follow_accept) |> select([r, u], u.follower_address) |> Repo.all() @@ -129,4 +142,58 @@ def move_following(origin, target) do move_following(origin, target) end end + + def all_between_user_sets( + source_users, + target_users + ) + when is_list(source_users) and is_list(target_users) do + source_user_ids = User.binary_id(source_users) + target_user_ids = User.binary_id(target_users) + + __MODULE__ + |> where( + fragment( + "(follower_id = ANY(?) AND following_id = ANY(?)) OR \ + (follower_id = ANY(?) AND following_id = ANY(?))", + ^source_user_ids, + ^target_user_ids, + ^target_user_ids, + ^source_user_ids + ) + ) + |> Repo.all() + end + + def find(following_relationships, follower, following) do + Enum.find(following_relationships, fn + fr -> fr.follower_id == follower.id and fr.following_id == following.id + end) + end + + defp validate_not_self_relationship(%Changeset{} = changeset) do + changeset + |> validate_follower_id_following_id_inequality() + |> validate_following_id_follower_id_inequality() + end + + defp validate_follower_id_following_id_inequality(%Changeset{} = changeset) do + validate_change(changeset, :follower_id, fn _, follower_id -> + if follower_id == get_field(changeset, :following_id) do + [source_id: "can't be equal to following_id"] + else + [] + end + end) + end + + defp validate_following_id_follower_id_inequality(%Changeset{} = changeset) do + validate_change(changeset, :following_id, fn _, following_id -> + if following_id == get_field(changeset, :follower_id) do + [target_id: "can't be equal to follower_id"] + else + [] + end + end) + end end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 0e5121694..df96f38df 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -227,6 +227,24 @@ def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \ end end + @doc """ + Dumps Flake Id to SQL-compatible format (16-byte UUID). + E.g. "9pQtDGXuq4p3VlcJEm" -> <<0, 0, 1, 110, 179, 218, 42, 92, 213, 41, 44, 227, 95, 213, 0, 0>> + """ + def binary_id(source_id) when is_binary(source_id) do + with {:ok, dumped_id} <- FlakeId.Ecto.CompatType.dump(source_id) do + dumped_id + else + _ -> source_id + end + end + + def binary_id(source_ids) when is_list(source_ids) do + Enum.map(source_ids, &binary_id/1) + end + + def binary_id(%User{} = user), do: binary_id(user.id) + @doc "Returns status account" @spec account_status(User.t()) :: account_status() def account_status(%User{deactivated: true}), do: :deactivated @@ -689,7 +707,7 @@ def needs_update?(_), do: true @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()} def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do - follow(follower, followed, "pending") + follow(follower, followed, :follow_pending) end def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do @@ -709,14 +727,14 @@ def maybe_direct_follow(%User{} = follower, %User{} = followed) do def follow_all(follower, followeds) do followeds |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end) - |> Enum.each(&follow(follower, &1, "accept")) + |> Enum.each(&follow(follower, &1, :follow_accept)) set_cache(follower) end defdelegate following(user), to: FollowingRelationship - def follow(%User{} = follower, %User{} = followed, state \\ "accept") do + def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked]) cond do @@ -743,7 +761,7 @@ def unfollow(%User{ap_id: ap_id}, %User{ap_id: ap_id}) do def unfollow(%User{} = follower, %User{} = followed) do case get_follow_state(follower, followed) do - state when state in ["accept", "pending"] -> + state when state in [:follow_pending, :follow_accept] -> FollowingRelationship.unfollow(follower, followed) {:ok, followed} = update_follower_count(followed) @@ -761,14 +779,18 @@ def unfollow(%User{} = follower, %User{} = followed) do defdelegate following?(follower, followed), to: FollowingRelationship + @doc "Returns follow state as Pleroma.FollowingRelationship.State value" def get_follow_state(%User{} = follower, %User{} = following) do following_relationship = FollowingRelationship.get(follower, following) case {following_relationship, following.local} do {nil, false} -> case Utils.fetch_latest_follow(follower, following) do - %{data: %{"state" => state}} when state in ["pending", "accept"] -> state - _ -> nil + %Activity{data: %{"state" => state}} when state in ["pending", "accept"] -> + FollowingRelationship.state_to_enum(state) + + _ -> + nil end {%{state: state}, _} -> @@ -1267,7 +1289,7 @@ def blocks?(nil, _), do: false def blocks?(%User{} = user, %User{} = target) do blocks_user?(user, target) || - (!User.following?(user, target) && blocks_domain?(user, target)) + (blocks_domain?(user, target) and not User.following?(user, target)) end def blocks_user?(%User{} = user, %User{} = target) do diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index 884e33039..ec88088cf 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -148,7 +148,7 @@ defp compose_query({:followers, %User{id: id}}, query) do as: :relationships, on: r.following_id == ^id and r.follower_id == u.id ) - |> where([relationships: r], r.state == "accept") + |> where([relationships: r], r.state == ^:follow_accept) end defp compose_query({:friends, %User{id: id}}, query) do @@ -158,7 +158,7 @@ defp compose_query({:friends, %User{id: id}}, query) do as: :relationships, on: r.following_id == u.id and r.follower_id == ^id ) - |> where([relationships: r], r.state == "accept") + |> where([relationships: r], r.state == ^:follow_accept) end defp compose_query({:recipients_from_activity, to}, query) do @@ -173,7 +173,7 @@ defp compose_query({:recipients_from_activity, to}, query) do ) |> where( [u, following: f, relationships: r], - u.ap_id in ^to or (f.follower_address in ^to and r.state == "accept") + u.ap_id in ^to or (f.follower_address in ^to and r.state == ^:follow_accept) ) |> distinct(true) end diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex index 01b6ace9d..ad0d303b1 100644 --- a/lib/pleroma/user_relationship.ex +++ b/lib/pleroma/user_relationship.ex @@ -8,6 +8,8 @@ defmodule Pleroma.UserRelationship do import Ecto.Changeset import Ecto.Query + alias Ecto.Changeset + alias Pleroma.FollowingRelationship alias Pleroma.Repo alias Pleroma.User alias Pleroma.UserRelationship @@ -15,12 +17,12 @@ defmodule Pleroma.UserRelationship do schema "user_relationships" do belongs_to(:source, User, type: FlakeId.Ecto.CompatType) belongs_to(:target, User, type: FlakeId.Ecto.CompatType) - field(:relationship_type, UserRelationshipTypeEnum) + field(:relationship_type, Pleroma.UserRelationship.Type) timestamps(updated_at: false) end - for relationship_type <- Keyword.keys(UserRelationshipTypeEnum.__enum_map__()) do + for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do # `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`, # `def create_notification_mute/2`, `def create_inverse_subscription/2` def unquote(:"create_#{relationship_type}")(source, target), @@ -37,6 +39,10 @@ def unquote(:"#{relationship_type}_exists?")(source, target), do: exists?(unquote(relationship_type), source, target) end + def user_relationship_types, do: Keyword.keys(user_relationship_mappings()) + + def user_relationship_mappings, do: Pleroma.UserRelationship.Type.__enum_map__() + def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do user_relationship |> cast(params, [:relationship_type, :source_id, :target_id]) @@ -75,18 +81,93 @@ def delete(relationship_type, %User{} = source, %User{} = target) do end end - defp validate_not_self_relationship(%Ecto.Changeset{} = changeset) do + def dictionary( + source_users, + target_users, + source_to_target_rel_types \\ nil, + target_to_source_rel_types \\ nil + ) + when is_list(source_users) and is_list(target_users) do + source_user_ids = User.binary_id(source_users) + target_user_ids = User.binary_id(target_users) + + get_rel_type_codes = fn rel_type -> user_relationship_mappings()[rel_type] end + + source_to_target_rel_types = + Enum.map(source_to_target_rel_types || user_relationship_types(), &get_rel_type_codes.(&1)) + + target_to_source_rel_types = + Enum.map(target_to_source_rel_types || user_relationship_types(), &get_rel_type_codes.(&1)) + + __MODULE__ + |> where( + fragment( + "(source_id = ANY(?) AND target_id = ANY(?) AND relationship_type = ANY(?)) OR \ + (source_id = ANY(?) AND target_id = ANY(?) AND relationship_type = ANY(?))", + ^source_user_ids, + ^target_user_ids, + ^source_to_target_rel_types, + ^target_user_ids, + ^source_user_ids, + ^target_to_source_rel_types + ) + ) + |> select([ur], [ur.relationship_type, ur.source_id, ur.target_id]) + |> Repo.all() + end + + def exists?(dictionary, rel_type, source, target, func) do + cond do + is_nil(source) or is_nil(target) -> + false + + dictionary -> + [rel_type, source.id, target.id] in dictionary + + true -> + func.(source, target) + end + end + + @doc ":relationships option for StatusView / AccountView / NotificationView" + def view_relationships_option(nil = _reading_user, _actors) do + %{user_relationships: [], following_relationships: []} + end + + def view_relationships_option(%User{} = reading_user, actors) do + user_relationships = + UserRelationship.dictionary( + [reading_user], + actors, + [:block, :mute, :notification_mute, :reblog_mute], + [:block, :inverse_subscription] + ) + + following_relationships = FollowingRelationship.all_between_user_sets([reading_user], actors) + + %{user_relationships: user_relationships, following_relationships: following_relationships} + end + + defp validate_not_self_relationship(%Changeset{} = changeset) do changeset - |> validate_change(:target_id, fn _, target_id -> - if target_id == get_field(changeset, :source_id) do - [target_id: "can't be equal to source_id"] + |> validate_source_id_target_id_inequality() + |> validate_target_id_source_id_inequality() + end + + defp validate_source_id_target_id_inequality(%Changeset{} = changeset) do + validate_change(changeset, :source_id, fn _, source_id -> + if source_id == get_field(changeset, :target_id) do + [source_id: "can't be equal to target_id"] else [] end end) - |> validate_change(:source_id, fn _, source_id -> - if source_id == get_field(changeset, :target_id) do - [source_id: "can't be equal to target_id"] + end + + defp validate_target_id_source_id_inequality(%Changeset{} = changeset) do + validate_change(changeset, :target_id, fn _, target_id -> + if target_id == get_field(changeset, :source_id) do + [target_id: "can't be equal to source_id"] else [] end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 09bd9a442..831739c5f 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -491,7 +491,8 @@ def handle_incoming( {_, {:ok, follower}} <- {:follow, User.follow(follower, followed)}, {_, {:ok, _}} <- {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")}, - {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do + {:ok, _relationship} <- + FollowingRelationship.update(follower, followed, :follow_accept) do ActivityPub.accept(%{ to: [follower.ap_id], actor: followed, @@ -501,7 +502,7 @@ def handle_incoming( else {:user_blocked, true} -> {:ok, _} = Utils.update_follow_state_for_all(activity, "reject") - {:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject") + {:ok, _relationship} = FollowingRelationship.update(follower, followed, :follow_reject) ActivityPub.reject(%{ to: [follower.ap_id], @@ -512,7 +513,7 @@ def handle_incoming( {:follow, {:error, _}} -> {:ok, _} = Utils.update_follow_state_for_all(activity, "reject") - {:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject") + {:ok, _relationship} = FollowingRelationship.update(follower, followed, :follow_reject) ActivityPub.reject(%{ to: [follower.ap_id], @@ -522,7 +523,7 @@ def handle_incoming( }) {:user_locked, true} -> - {:ok, _relationship} = FollowingRelationship.update(follower, followed, "pending") + {:ok, _relationship} = FollowingRelationship.update(follower, followed, :follow_pending) :noop end @@ -542,7 +543,7 @@ def handle_incoming( {:ok, follow_activity} <- get_follow_activity(follow_object, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), - {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept) do ActivityPub.accept(%{ to: follow_activity.data["to"], type: "Accept", @@ -565,7 +566,7 @@ def handle_incoming( {:ok, follow_activity} <- get_follow_activity(follow_object, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), - {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"), + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject), {:ok, activity} <- ActivityPub.reject(%{ to: follow_activity.data["to"], diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index b9fa9fe3a..c84782008 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -42,7 +42,7 @@ def accept_follow_request(follower, followed) do with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), {:ok, follower} <- User.follow(follower, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), - {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept"), + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept), {:ok, _activity} <- ActivityPub.accept(%{ to: [follower.ap_id], @@ -57,7 +57,7 @@ def accept_follow_request(follower, followed) do def reject_follow_request(follower, followed) do with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), - {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"), + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject), {:ok, _activity} <- ActivityPub.reject(%{ to: [follower.ap_id], diff --git a/priv/repo/migrations/20200328124805_change_following_relationships_state_to_integer.exs b/priv/repo/migrations/20200328124805_change_following_relationships_state_to_integer.exs new file mode 100644 index 000000000..2b0820f3f --- /dev/null +++ b/priv/repo/migrations/20200328124805_change_following_relationships_state_to_integer.exs @@ -0,0 +1,29 @@ +defmodule Pleroma.Repo.Migrations.ChangeFollowingRelationshipsStateToInteger do + use Ecto.Migration + + @alter_following_relationship_state "ALTER TABLE following_relationships ALTER COLUMN state" + + def up do + execute(""" + #{@alter_following_relationship_state} TYPE integer USING + CASE + WHEN state = 'pending' THEN 1 + WHEN state = 'accept' THEN 2 + WHEN state = 'reject' THEN 3 + ELSE 0 + END; + """) + end + + def down do + execute(""" + #{@alter_following_relationship_state} TYPE varchar(255) USING + CASE + WHEN state = 1 THEN 'pending' + WHEN state = 2 THEN 'accept' + WHEN state = 3 THEN 'reject' + ELSE '' + END; + """) + end +end diff --git a/priv/repo/migrations/20200328130139_add_following_relationships_following_id_index.exs b/priv/repo/migrations/20200328130139_add_following_relationships_following_id_index.exs new file mode 100644 index 000000000..884832f84 --- /dev/null +++ b/priv/repo/migrations/20200328130139_add_following_relationships_following_id_index.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.AddFollowingRelationshipsFollowingIdIndex do + use Ecto.Migration + + # [:follower_index] index is useless because of [:follower_id, :following_id] index + # [:following_id] index makes sense because of user's followers-targeted queries + def change do + drop_if_exists(index(:following_relationships, [:follower_id])) + + create_if_not_exists(index(:following_relationships, [:following_id])) + end +end diff --git a/test/following_relationship_test.exs b/test/following_relationship_test.exs index 865bb3838..17a468abb 100644 --- a/test/following_relationship_test.exs +++ b/test/following_relationship_test.exs @@ -15,28 +15,28 @@ defmodule Pleroma.FollowingRelationshipTest do test "returns following addresses without internal.fetch" do user = insert(:user) fetch_actor = InternalFetchActor.get_actor() - FollowingRelationship.follow(fetch_actor, user, "accept") + FollowingRelationship.follow(fetch_actor, user, :follow_accept) assert FollowingRelationship.following(fetch_actor) == [user.follower_address] end test "returns following addresses without relay" do user = insert(:user) relay_actor = Relay.get_actor() - FollowingRelationship.follow(relay_actor, user, "accept") + FollowingRelationship.follow(relay_actor, user, :follow_accept) assert FollowingRelationship.following(relay_actor) == [user.follower_address] end test "returns following addresses without remote user" do user = insert(:user) actor = insert(:user, local: false) - FollowingRelationship.follow(actor, user, "accept") + FollowingRelationship.follow(actor, user, :follow_accept) assert FollowingRelationship.following(actor) == [user.follower_address] end test "returns following addresses with local user" do user = insert(:user) actor = insert(:user, local: true) - FollowingRelationship.follow(actor, user, "accept") + FollowingRelationship.follow(actor, user, :follow_accept) assert FollowingRelationship.following(actor) == [ actor.follower_address, diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs index 22030a423..0f6ffb2b1 100644 --- a/test/tasks/user_test.exs +++ b/test/tasks/user_test.exs @@ -140,7 +140,7 @@ test "no user to toggle" do test "user is unsubscribed" do followed = insert(:user) user = insert(:user) - User.follow(user, followed, "accept") + User.follow(user, followed, :follow_accept) Mix.Tasks.Pleroma.User.run(["unsubscribe", user.nickname]) diff --git a/test/user_test.exs b/test/user_test.exs index c48821a3c..e63c44360 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -194,7 +194,8 @@ test "doesn't return already accepted or duplicate follow requests" do CommonAPI.follow(pending_follower, locked) CommonAPI.follow(pending_follower, locked) CommonAPI.follow(accepted_follower, locked) - Pleroma.FollowingRelationship.update(accepted_follower, locked, "accept") + + Pleroma.FollowingRelationship.update(accepted_follower, locked, :follow_accept) assert [^pending_follower] = User.get_follow_requests(locked) end @@ -319,7 +320,7 @@ test "unfollow with syncronizes external user" do following_address: "http://localhost:4001/users/fuser2/following" }) - {:ok, user} = User.follow(user, followed, "accept") + {:ok, user} = User.follow(user, followed, :follow_accept) {:ok, user, _activity} = User.unfollow(user, followed) @@ -332,7 +333,7 @@ test "unfollow takes a user and another user" do followed = insert(:user) user = insert(:user) - {:ok, user} = User.follow(user, followed, "accept") + {:ok, user} = User.follow(user, followed, :follow_accept) assert User.following(user) == [user.follower_address, followed.follower_address] @@ -353,7 +354,7 @@ test "unfollow doesn't unfollow yourself" do test "test if a user is following another user" do followed = insert(:user) user = insert(:user) - User.follow(user, followed, "accept") + User.follow(user, followed, :follow_accept) assert User.following?(user, followed) refute User.following?(followed, user) diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 2baf9ce03..2a3fd92b4 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -1629,7 +1629,7 @@ test "it upgrades a user to activitypub" do }) user_two = insert(:user) - Pleroma.FollowingRelationship.follow(user_two, user, "accept") + Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept) {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"}) diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 9fe8fc0f3..d4d605251 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -574,7 +574,7 @@ test "cancels a pending follow for a local user" do assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} = CommonAPI.follow(follower, followed) - assert User.get_follow_state(follower, followed) == "pending" + assert User.get_follow_state(follower, followed) == :follow_pending assert {:ok, follower} = CommonAPI.unfollow(follower, followed) assert User.get_follow_state(follower, followed) == nil @@ -596,7 +596,7 @@ test "cancels a pending follow for a remote user" do assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} = CommonAPI.follow(follower, followed) - assert User.get_follow_state(follower, followed) == "pending" + assert User.get_follow_state(follower, followed) == :follow_pending assert {:ok, follower} = CommonAPI.unfollow(follower, followed) assert User.get_follow_state(follower, followed) == nil diff --git a/test/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/web/mastodon_api/controllers/follow_request_controller_test.exs index dd848821a..d8dbe4800 100644 --- a/test/web/mastodon_api/controllers/follow_request_controller_test.exs +++ b/test/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -21,7 +21,7 @@ test "/api/v1/follow_requests works", %{user: user, conn: conn} do other_user = insert(:user) {:ok, _activity} = ActivityPub.follow(other_user, user) - {:ok, other_user} = User.follow(other_user, user, "pending") + {:ok, other_user} = User.follow(other_user, user, :follow_pending) assert User.following?(other_user, user) == false @@ -35,7 +35,7 @@ test "/api/v1/follow_requests/:id/authorize works", %{user: user, conn: conn} do other_user = insert(:user) {:ok, _activity} = ActivityPub.follow(other_user, user) - {:ok, other_user} = User.follow(other_user, user, "pending") + {:ok, other_user} = User.follow(other_user, user, :follow_pending) user = User.get_cached_by_id(user.id) other_user = User.get_cached_by_id(other_user.id) diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs index 339f99bbf..720f8fa44 100644 --- a/test/web/streamer/streamer_test.exs +++ b/test/web/streamer/streamer_test.exs @@ -198,7 +198,7 @@ test "it doesn't send to user if recipients invalid and thread containment is en Pleroma.Config.put([:instance, :skip_thread_containment], false) author = insert(:user) user = insert(:user) - User.follow(user, author, "accept") + User.follow(user, author, :follow_accept) activity = insert(:note_activity, @@ -221,7 +221,7 @@ test "it sends message if recipients invalid and thread containment is disabled" Pleroma.Config.put([:instance, :skip_thread_containment], true) author = insert(:user) user = insert(:user) - User.follow(user, author, "accept") + User.follow(user, author, :follow_accept) activity = insert(:note_activity, @@ -244,7 +244,7 @@ test "it sends message if recipients invalid and thread containment is enabled b Pleroma.Config.put([:instance, :skip_thread_containment], false) author = insert(:user) user = insert(:user, skip_thread_containment: true) - User.follow(user, author, "accept") + User.follow(user, author, :follow_accept) activity = insert(:note_activity, -- cgit v1.2.3 From 06fbb957151a84e08e511decf4e9ae693bc25f5f Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 29 Apr 2020 15:49:48 -0500 Subject: Update AdminFE build in preparation for the 2.0.3 release --- priv/static/adminfe/app.796ca6d4.css | Bin 0 -> 12837 bytes priv/static/adminfe/app.85534e14.css | Bin 12836 -> 0 bytes priv/static/adminfe/chunk-0558.af0d89cd.css | Bin 0 -> 4748 bytes priv/static/adminfe/chunk-0778.d9e7180a.css | Bin 0 -> 2340 bytes priv/static/adminfe/chunk-0961.d3692214.css | Bin 0 -> 2044 bytes priv/static/adminfe/chunk-0d8f.d85f5a29.css | Bin 3433 -> 0 bytes priv/static/adminfe/chunk-136a.f1130f8e.css | Bin 4946 -> 0 bytes priv/static/adminfe/chunk-13e9.98eaadba.css | Bin 1071 -> 0 bytes priv/static/adminfe/chunk-15fa.5a5f973d.css | Bin 4748 -> 0 bytes priv/static/adminfe/chunk-22d2.813009b9.css | Bin 0 -> 6282 bytes priv/static/adminfe/chunk-2b9c.feb61a2b.css | Bin 5580 -> 0 bytes priv/static/adminfe/chunk-3384.2278f87c.css | Bin 0 -> 5550 bytes priv/static/adminfe/chunk-4011.c4799067.css | Bin 0 -> 23982 bytes priv/static/adminfe/chunk-46ef.145de4f9.css | Bin 1790 -> 0 bytes priv/static/adminfe/chunk-4ffb.dd09fe2e.css | Bin 745 -> 0 bytes priv/static/adminfe/chunk-6b68.0cc00484.css | Bin 0 -> 692 bytes priv/static/adminfe/chunk-6e81.0e80d020.css | Bin 0 -> 745 bytes priv/static/adminfe/chunk-7637.941c4edb.css | Bin 0 -> 1347 bytes priv/static/adminfe/chunk-876c.90dffac4.css | Bin 2044 -> 0 bytes priv/static/adminfe/chunk-87b3.3c6ede9c.css | Bin 9575 -> 0 bytes priv/static/adminfe/chunk-88c9.184084df.css | Bin 5731 -> 0 bytes priv/static/adminfe/chunk-970d.f59cca8c.css | Bin 0 -> 6173 bytes priv/static/adminfe/chunk-cf57.26596375.css | Bin 3244 -> 0 bytes priv/static/adminfe/chunk-d38a.cabdc22e.css | Bin 0 -> 3332 bytes priv/static/adminfe/chunk-e458.f88bafea.css | Bin 0 -> 3156 bytes priv/static/adminfe/index.html | 2 +- priv/static/adminfe/static/js/app.203f69f8.js | Bin 0 -> 187722 bytes priv/static/adminfe/static/js/app.203f69f8.js.map | Bin 0 -> 416278 bytes priv/static/adminfe/static/js/app.d898cc2b.js | Bin 185128 -> 0 bytes priv/static/adminfe/static/js/app.d898cc2b.js.map | Bin 410154 -> 0 bytes priv/static/adminfe/static/js/chunk-0558.75954137.js | Bin 0 -> 7919 bytes .../adminfe/static/js/chunk-0558.75954137.js.map | Bin 0 -> 17438 bytes priv/static/adminfe/static/js/chunk-0778.b17650df.js | Bin 0 -> 9756 bytes .../adminfe/static/js/chunk-0778.b17650df.js.map | Bin 0 -> 32393 bytes priv/static/adminfe/static/js/chunk-0961.ef33e81b.js | Bin 0 -> 5112 bytes .../adminfe/static/js/chunk-0961.ef33e81b.js.map | Bin 0 -> 19744 bytes priv/static/adminfe/static/js/chunk-0d8f.6d50ff86.js | Bin 33538 -> 0 bytes .../adminfe/static/js/chunk-0d8f.6d50ff86.js.map | Bin 116201 -> 0 bytes priv/static/adminfe/static/js/chunk-136a.c4719e3e.js | Bin 19553 -> 0 bytes .../adminfe/static/js/chunk-136a.c4719e3e.js.map | Bin 69090 -> 0 bytes priv/static/adminfe/static/js/chunk-13e9.79da1569.js | Bin 9528 -> 0 bytes .../adminfe/static/js/chunk-13e9.79da1569.js.map | Bin 40125 -> 0 bytes priv/static/adminfe/static/js/chunk-15fa.34070731.js | Bin 7919 -> 0 bytes .../adminfe/static/js/chunk-15fa.34070731.js.map | Bin 17438 -> 0 bytes priv/static/adminfe/static/js/chunk-22d2.a0cf7976.js | Bin 0 -> 30624 bytes .../adminfe/static/js/chunk-22d2.a0cf7976.js.map | Bin 0 -> 103450 bytes priv/static/adminfe/static/js/chunk-2b9c.cf321c74.js | Bin 28194 -> 0 bytes .../adminfe/static/js/chunk-2b9c.cf321c74.js.map | Bin 95810 -> 0 bytes priv/static/adminfe/static/js/chunk-3384.458ffaf1.js | Bin 0 -> 23953 bytes .../adminfe/static/js/chunk-3384.458ffaf1.js.map | Bin 0 -> 85906 bytes priv/static/adminfe/static/js/chunk-4011.67fb1692.js | Bin 0 -> 117521 bytes .../adminfe/static/js/chunk-4011.67fb1692.js.map | Bin 0 -> 397967 bytes priv/static/adminfe/static/js/chunk-46ef.671cac7d.js | Bin 7765 -> 0 bytes .../adminfe/static/js/chunk-46ef.671cac7d.js.map | Bin 26170 -> 0 bytes priv/static/adminfe/static/js/chunk-4ffb.0e8f3772.js | Bin 2080 -> 0 bytes .../adminfe/static/js/chunk-4ffb.0e8f3772.js.map | Bin 9090 -> 0 bytes priv/static/adminfe/static/js/chunk-6b68.fbc0f684.js | Bin 0 -> 14790 bytes .../adminfe/static/js/chunk-6b68.fbc0f684.js.map | Bin 0 -> 40172 bytes priv/static/adminfe/static/js/chunk-6e81.3733ace2.js | Bin 0 -> 2080 bytes .../adminfe/static/js/chunk-6e81.3733ace2.js.map | Bin 0 -> 9090 bytes priv/static/adminfe/static/js/chunk-7637.8f5fb36e.js | Bin 0 -> 10877 bytes .../adminfe/static/js/chunk-7637.8f5fb36e.js.map | Bin 0 -> 44563 bytes priv/static/adminfe/static/js/chunk-876c.e4ceccca.js | Bin 5112 -> 0 bytes .../adminfe/static/js/chunk-876c.e4ceccca.js.map | Bin 19744 -> 0 bytes priv/static/adminfe/static/js/chunk-87b3.3c11ef09.js | Bin 103449 -> 0 bytes .../adminfe/static/js/chunk-87b3.3c11ef09.js.map | Bin 358904 -> 0 bytes priv/static/adminfe/static/js/chunk-88c9.e3583744.js | Bin 24234 -> 0 bytes .../adminfe/static/js/chunk-88c9.e3583744.js.map | Bin 92387 -> 0 bytes priv/static/adminfe/static/js/chunk-970d.2457e066.js | Bin 0 -> 26608 bytes .../adminfe/static/js/chunk-970d.2457e066.js.map | Bin 0 -> 100000 bytes priv/static/adminfe/static/js/chunk-cf57.3e45f57f.js | Bin 29728 -> 0 bytes .../adminfe/static/js/chunk-cf57.3e45f57f.js.map | Bin 89855 -> 0 bytes priv/static/adminfe/static/js/chunk-d38a.a851004a.js | Bin 0 -> 20205 bytes .../adminfe/static/js/chunk-d38a.a851004a.js.map | Bin 0 -> 81345 bytes priv/static/adminfe/static/js/chunk-e458.4e5aad44.js | Bin 0 -> 16756 bytes .../adminfe/static/js/chunk-e458.4e5aad44.js.map | Bin 0 -> 55666 bytes priv/static/adminfe/static/js/runtime.1b4f6ce0.js | Bin 0 -> 4032 bytes priv/static/adminfe/static/js/runtime.1b4f6ce0.js.map | Bin 0 -> 16879 bytes priv/static/adminfe/static/js/runtime.cb26bbd1.js | Bin 3969 -> 0 bytes priv/static/adminfe/static/js/runtime.cb26bbd1.js.map | Bin 16759 -> 0 bytes 80 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 priv/static/adminfe/app.796ca6d4.css delete mode 100644 priv/static/adminfe/app.85534e14.css create mode 100644 priv/static/adminfe/chunk-0558.af0d89cd.css create mode 100644 priv/static/adminfe/chunk-0778.d9e7180a.css create mode 100644 priv/static/adminfe/chunk-0961.d3692214.css delete mode 100644 priv/static/adminfe/chunk-0d8f.d85f5a29.css delete mode 100644 priv/static/adminfe/chunk-136a.f1130f8e.css delete mode 100644 priv/static/adminfe/chunk-13e9.98eaadba.css delete mode 100644 priv/static/adminfe/chunk-15fa.5a5f973d.css create mode 100644 priv/static/adminfe/chunk-22d2.813009b9.css delete mode 100644 priv/static/adminfe/chunk-2b9c.feb61a2b.css create mode 100644 priv/static/adminfe/chunk-3384.2278f87c.css create mode 100644 priv/static/adminfe/chunk-4011.c4799067.css delete mode 100644 priv/static/adminfe/chunk-46ef.145de4f9.css delete mode 100644 priv/static/adminfe/chunk-4ffb.dd09fe2e.css create mode 100644 priv/static/adminfe/chunk-6b68.0cc00484.css create mode 100644 priv/static/adminfe/chunk-6e81.0e80d020.css create mode 100644 priv/static/adminfe/chunk-7637.941c4edb.css delete mode 100644 priv/static/adminfe/chunk-876c.90dffac4.css delete mode 100644 priv/static/adminfe/chunk-87b3.3c6ede9c.css delete mode 100644 priv/static/adminfe/chunk-88c9.184084df.css create mode 100644 priv/static/adminfe/chunk-970d.f59cca8c.css delete mode 100644 priv/static/adminfe/chunk-cf57.26596375.css create mode 100644 priv/static/adminfe/chunk-d38a.cabdc22e.css create mode 100644 priv/static/adminfe/chunk-e458.f88bafea.css create mode 100644 priv/static/adminfe/static/js/app.203f69f8.js create mode 100644 priv/static/adminfe/static/js/app.203f69f8.js.map delete mode 100644 priv/static/adminfe/static/js/app.d898cc2b.js delete mode 100644 priv/static/adminfe/static/js/app.d898cc2b.js.map create mode 100644 priv/static/adminfe/static/js/chunk-0558.75954137.js create mode 100644 priv/static/adminfe/static/js/chunk-0558.75954137.js.map create mode 100644 priv/static/adminfe/static/js/chunk-0778.b17650df.js create mode 100644 priv/static/adminfe/static/js/chunk-0778.b17650df.js.map create mode 100644 priv/static/adminfe/static/js/chunk-0961.ef33e81b.js create mode 100644 priv/static/adminfe/static/js/chunk-0961.ef33e81b.js.map delete mode 100644 priv/static/adminfe/static/js/chunk-0d8f.6d50ff86.js delete mode 100644 priv/static/adminfe/static/js/chunk-0d8f.6d50ff86.js.map delete mode 100644 priv/static/adminfe/static/js/chunk-136a.c4719e3e.js delete mode 100644 priv/static/adminfe/static/js/chunk-136a.c4719e3e.js.map delete mode 100644 priv/static/adminfe/static/js/chunk-13e9.79da1569.js delete mode 100644 priv/static/adminfe/static/js/chunk-13e9.79da1569.js.map delete mode 100644 priv/static/adminfe/static/js/chunk-15fa.34070731.js delete mode 100644 priv/static/adminfe/static/js/chunk-15fa.34070731.js.map create mode 100644 priv/static/adminfe/static/js/chunk-22d2.a0cf7976.js create mode 100644 priv/static/adminfe/static/js/chunk-22d2.a0cf7976.js.map delete mode 100644 priv/static/adminfe/static/js/chunk-2b9c.cf321c74.js delete mode 100644 priv/static/adminfe/static/js/chunk-2b9c.cf321c74.js.map create mode 100644 priv/static/adminfe/static/js/chunk-3384.458ffaf1.js create mode 100644 priv/static/adminfe/static/js/chunk-3384.458ffaf1.js.map create mode 100644 priv/static/adminfe/static/js/chunk-4011.67fb1692.js create mode 100644 priv/static/adminfe/static/js/chunk-4011.67fb1692.js.map delete mode 100644 priv/static/adminfe/static/js/chunk-46ef.671cac7d.js delete mode 100644 priv/static/adminfe/static/js/chunk-46ef.671cac7d.js.map delete mode 100644 priv/static/adminfe/static/js/chunk-4ffb.0e8f3772.js delete mode 100644 priv/static/adminfe/static/js/chunk-4ffb.0e8f3772.js.map create mode 100644 priv/static/adminfe/static/js/chunk-6b68.fbc0f684.js create mode 100644 priv/static/adminfe/static/js/chunk-6b68.fbc0f684.js.map create mode 100644 priv/static/adminfe/static/js/chunk-6e81.3733ace2.js create mode 100644 priv/static/adminfe/static/js/chunk-6e81.3733ace2.js.map create mode 100644 priv/static/adminfe/static/js/chunk-7637.8f5fb36e.js create mode 100644 priv/static/adminfe/static/js/chunk-7637.8f5fb36e.js.map delete mode 100644 priv/static/adminfe/static/js/chunk-876c.e4ceccca.js delete mode 100644 priv/static/adminfe/static/js/chunk-876c.e4ceccca.js.map delete mode 100644 priv/static/adminfe/static/js/chunk-87b3.3c11ef09.js delete mode 100644 priv/static/adminfe/static/js/chunk-87b3.3c11ef09.js.map delete mode 100644 priv/static/adminfe/static/js/chunk-88c9.e3583744.js delete mode 100644 priv/static/adminfe/static/js/chunk-88c9.e3583744.js.map create mode 100644 priv/static/adminfe/static/js/chunk-970d.2457e066.js create mode 100644 priv/static/adminfe/static/js/chunk-970d.2457e066.js.map delete mode 100644 priv/static/adminfe/static/js/chunk-cf57.3e45f57f.js delete mode 100644 priv/static/adminfe/static/js/chunk-cf57.3e45f57f.js.map create mode 100644 priv/static/adminfe/static/js/chunk-d38a.a851004a.js create mode 100644 priv/static/adminfe/static/js/chunk-d38a.a851004a.js.map create mode 100644 priv/static/adminfe/static/js/chunk-e458.4e5aad44.js create mode 100644 priv/static/adminfe/static/js/chunk-e458.4e5aad44.js.map create mode 100644 priv/static/adminfe/static/js/runtime.1b4f6ce0.js create mode 100644 priv/static/adminfe/static/js/runtime.1b4f6ce0.js.map delete mode 100644 priv/static/adminfe/static/js/runtime.cb26bbd1.js delete mode 100644 priv/static/adminfe/static/js/runtime.cb26bbd1.js.map diff --git a/priv/static/adminfe/app.796ca6d4.css b/priv/static/adminfe/app.796ca6d4.css new file mode 100644 index 000000000..1b83a8a39 Binary files /dev/null and b/priv/static/adminfe/app.796ca6d4.css differ diff --git a/priv/static/adminfe/app.85534e14.css b/priv/static/adminfe/app.85534e14.css deleted file mode 100644 index 473ec1b86..000000000 Binary files a/priv/static/adminfe/app.85534e14.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-0558.af0d89cd.css b/priv/static/adminfe/chunk-0558.af0d89cd.css new file mode 100644 index 000000000..30bf7de23 Binary files /dev/null and b/priv/static/adminfe/chunk-0558.af0d89cd.css differ diff --git a/priv/static/adminfe/chunk-0778.d9e7180a.css b/priv/static/adminfe/chunk-0778.d9e7180a.css new file mode 100644 index 000000000..9d730019a Binary files /dev/null and b/priv/static/adminfe/chunk-0778.d9e7180a.css differ diff --git a/priv/static/adminfe/chunk-0961.d3692214.css b/priv/static/adminfe/chunk-0961.d3692214.css new file mode 100644 index 000000000..c0074e6f7 Binary files /dev/null and b/priv/static/adminfe/chunk-0961.d3692214.css differ diff --git a/priv/static/adminfe/chunk-0d8f.d85f5a29.css b/priv/static/adminfe/chunk-0d8f.d85f5a29.css deleted file mode 100644 index 931620872..000000000 Binary files a/priv/static/adminfe/chunk-0d8f.d85f5a29.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-136a.f1130f8e.css b/priv/static/adminfe/chunk-136a.f1130f8e.css deleted file mode 100644 index f492b37d0..000000000 Binary files a/priv/static/adminfe/chunk-136a.f1130f8e.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-13e9.98eaadba.css b/priv/static/adminfe/chunk-13e9.98eaadba.css deleted file mode 100644 index 9f377eee2..000000000 Binary files a/priv/static/adminfe/chunk-13e9.98eaadba.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-15fa.5a5f973d.css b/priv/static/adminfe/chunk-15fa.5a5f973d.css deleted file mode 100644 index 30bf7de23..000000000 Binary files a/priv/static/adminfe/chunk-15fa.5a5f973d.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-22d2.813009b9.css b/priv/static/adminfe/chunk-22d2.813009b9.css new file mode 100644 index 000000000..f0a98583e Binary files /dev/null and b/priv/static/adminfe/chunk-22d2.813009b9.css differ diff --git a/priv/static/adminfe/chunk-2b9c.feb61a2b.css b/priv/static/adminfe/chunk-2b9c.feb61a2b.css deleted file mode 100644 index f54eca1f5..000000000 Binary files a/priv/static/adminfe/chunk-2b9c.feb61a2b.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-3384.2278f87c.css b/priv/static/adminfe/chunk-3384.2278f87c.css new file mode 100644 index 000000000..96e3273eb Binary files /dev/null and b/priv/static/adminfe/chunk-3384.2278f87c.css differ diff --git a/priv/static/adminfe/chunk-4011.c4799067.css b/priv/static/adminfe/chunk-4011.c4799067.css new file mode 100644 index 000000000..1fb099c0c Binary files /dev/null and b/priv/static/adminfe/chunk-4011.c4799067.css differ diff --git a/priv/static/adminfe/chunk-46ef.145de4f9.css b/priv/static/adminfe/chunk-46ef.145de4f9.css deleted file mode 100644 index deb5249ac..000000000 Binary files a/priv/static/adminfe/chunk-46ef.145de4f9.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-4ffb.dd09fe2e.css b/priv/static/adminfe/chunk-4ffb.dd09fe2e.css deleted file mode 100644 index da819ca09..000000000 Binary files a/priv/static/adminfe/chunk-4ffb.dd09fe2e.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-6b68.0cc00484.css b/priv/static/adminfe/chunk-6b68.0cc00484.css new file mode 100644 index 000000000..7061b3d03 Binary files /dev/null and b/priv/static/adminfe/chunk-6b68.0cc00484.css differ diff --git a/priv/static/adminfe/chunk-6e81.0e80d020.css b/priv/static/adminfe/chunk-6e81.0e80d020.css new file mode 100644 index 000000000..da819ca09 Binary files /dev/null and b/priv/static/adminfe/chunk-6e81.0e80d020.css differ diff --git a/priv/static/adminfe/chunk-7637.941c4edb.css b/priv/static/adminfe/chunk-7637.941c4edb.css new file mode 100644 index 000000000..be1d183a9 Binary files /dev/null and b/priv/static/adminfe/chunk-7637.941c4edb.css differ diff --git a/priv/static/adminfe/chunk-876c.90dffac4.css b/priv/static/adminfe/chunk-876c.90dffac4.css deleted file mode 100644 index c0074e6f7..000000000 Binary files a/priv/static/adminfe/chunk-876c.90dffac4.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-87b3.3c6ede9c.css b/priv/static/adminfe/chunk-87b3.3c6ede9c.css deleted file mode 100644 index f0e6bf4ee..000000000 Binary files a/priv/static/adminfe/chunk-87b3.3c6ede9c.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-88c9.184084df.css b/priv/static/adminfe/chunk-88c9.184084df.css deleted file mode 100644 index f3299f33b..000000000 Binary files a/priv/static/adminfe/chunk-88c9.184084df.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-970d.f59cca8c.css b/priv/static/adminfe/chunk-970d.f59cca8c.css new file mode 100644 index 000000000..15511f12f Binary files /dev/null and b/priv/static/adminfe/chunk-970d.f59cca8c.css differ diff --git a/priv/static/adminfe/chunk-cf57.26596375.css b/priv/static/adminfe/chunk-cf57.26596375.css deleted file mode 100644 index 9f72b88c1..000000000 Binary files a/priv/static/adminfe/chunk-cf57.26596375.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-d38a.cabdc22e.css b/priv/static/adminfe/chunk-d38a.cabdc22e.css new file mode 100644 index 000000000..4a2bf472b Binary files /dev/null and b/priv/static/adminfe/chunk-d38a.cabdc22e.css differ diff --git a/priv/static/adminfe/chunk-e458.f88bafea.css b/priv/static/adminfe/chunk-e458.f88bafea.css new file mode 100644 index 000000000..085bdf076 Binary files /dev/null and b/priv/static/adminfe/chunk-e458.f88bafea.css differ diff --git a/priv/static/adminfe/index.html b/priv/static/adminfe/index.html index 3651c1cf0..a236dd0f7 100644 --- a/priv/static/adminfe/index.html +++ b/priv/static/adminfe/index.html @@ -1 +1 @@ -Admin FE
\ No newline at end of file +Admin FE
\ No newline at end of file diff --git a/priv/static/adminfe/static/js/app.203f69f8.js b/priv/static/adminfe/static/js/app.203f69f8.js new file mode 100644 index 000000000..d06fdf71d Binary files /dev/null and b/priv/static/adminfe/static/js/app.203f69f8.js differ diff --git a/priv/static/adminfe/static/js/app.203f69f8.js.map b/priv/static/adminfe/static/js/app.203f69f8.js.map new file mode 100644 index 000000000..eb78cd464 Binary files /dev/null and b/priv/static/adminfe/static/js/app.203f69f8.js.map differ diff --git a/priv/static/adminfe/static/js/app.d898cc2b.js b/priv/static/adminfe/static/js/app.d898cc2b.js deleted file mode 100644 index 9d60db06b..000000000 Binary files a/priv/static/adminfe/static/js/app.d898cc2b.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/app.d898cc2b.js.map b/priv/static/adminfe/static/js/app.d898cc2b.js.map deleted file mode 100644 index 1c4ec7590..000000000 Binary files a/priv/static/adminfe/static/js/app.d898cc2b.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0558.75954137.js b/priv/static/adminfe/static/js/chunk-0558.75954137.js new file mode 100644 index 000000000..7b29707fa Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0558.75954137.js differ diff --git a/priv/static/adminfe/static/js/chunk-0558.75954137.js.map b/priv/static/adminfe/static/js/chunk-0558.75954137.js.map new file mode 100644 index 000000000..e9e2affb6 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0558.75954137.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-0778.b17650df.js b/priv/static/adminfe/static/js/chunk-0778.b17650df.js new file mode 100644 index 000000000..1a174cc1e Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0778.b17650df.js differ diff --git a/priv/static/adminfe/static/js/chunk-0778.b17650df.js.map b/priv/static/adminfe/static/js/chunk-0778.b17650df.js.map new file mode 100644 index 000000000..1f96c3236 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0778.b17650df.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-0961.ef33e81b.js b/priv/static/adminfe/static/js/chunk-0961.ef33e81b.js new file mode 100644 index 000000000..e090bb93c Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0961.ef33e81b.js differ diff --git a/priv/static/adminfe/static/js/chunk-0961.ef33e81b.js.map b/priv/static/adminfe/static/js/chunk-0961.ef33e81b.js.map new file mode 100644 index 000000000..97c6a4b54 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0961.ef33e81b.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-0d8f.6d50ff86.js b/priv/static/adminfe/static/js/chunk-0d8f.6d50ff86.js deleted file mode 100644 index 4b0945f57..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0d8f.6d50ff86.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0d8f.6d50ff86.js.map b/priv/static/adminfe/static/js/chunk-0d8f.6d50ff86.js.map deleted file mode 100644 index da24cbef5..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0d8f.6d50ff86.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-136a.c4719e3e.js b/priv/static/adminfe/static/js/chunk-136a.c4719e3e.js deleted file mode 100644 index 0c2f1a52e..000000000 Binary files a/priv/static/adminfe/static/js/chunk-136a.c4719e3e.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-136a.c4719e3e.js.map b/priv/static/adminfe/static/js/chunk-136a.c4719e3e.js.map deleted file mode 100644 index 4b137fd49..000000000 Binary files a/priv/static/adminfe/static/js/chunk-136a.c4719e3e.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-13e9.79da1569.js b/priv/static/adminfe/static/js/chunk-13e9.79da1569.js deleted file mode 100644 index b98177b82..000000000 Binary files a/priv/static/adminfe/static/js/chunk-13e9.79da1569.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-13e9.79da1569.js.map b/priv/static/adminfe/static/js/chunk-13e9.79da1569.js.map deleted file mode 100644 index 118a47034..000000000 Binary files a/priv/static/adminfe/static/js/chunk-13e9.79da1569.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-15fa.34070731.js b/priv/static/adminfe/static/js/chunk-15fa.34070731.js deleted file mode 100644 index 937908d00..000000000 Binary files a/priv/static/adminfe/static/js/chunk-15fa.34070731.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-15fa.34070731.js.map b/priv/static/adminfe/static/js/chunk-15fa.34070731.js.map deleted file mode 100644 index d3830be7c..000000000 Binary files a/priv/static/adminfe/static/js/chunk-15fa.34070731.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-22d2.a0cf7976.js b/priv/static/adminfe/static/js/chunk-22d2.a0cf7976.js new file mode 100644 index 000000000..903f553b0 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-22d2.a0cf7976.js differ diff --git a/priv/static/adminfe/static/js/chunk-22d2.a0cf7976.js.map b/priv/static/adminfe/static/js/chunk-22d2.a0cf7976.js.map new file mode 100644 index 000000000..68735ed26 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-22d2.a0cf7976.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-2b9c.cf321c74.js b/priv/static/adminfe/static/js/chunk-2b9c.cf321c74.js deleted file mode 100644 index f06da0268..000000000 Binary files a/priv/static/adminfe/static/js/chunk-2b9c.cf321c74.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-2b9c.cf321c74.js.map b/priv/static/adminfe/static/js/chunk-2b9c.cf321c74.js.map deleted file mode 100644 index 1ec750dd1..000000000 Binary files a/priv/static/adminfe/static/js/chunk-2b9c.cf321c74.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-3384.458ffaf1.js b/priv/static/adminfe/static/js/chunk-3384.458ffaf1.js new file mode 100644 index 000000000..eb2b55d37 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-3384.458ffaf1.js differ diff --git a/priv/static/adminfe/static/js/chunk-3384.458ffaf1.js.map b/priv/static/adminfe/static/js/chunk-3384.458ffaf1.js.map new file mode 100644 index 000000000..0bb577aab Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-3384.458ffaf1.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-4011.67fb1692.js b/priv/static/adminfe/static/js/chunk-4011.67fb1692.js new file mode 100644 index 000000000..775ed26f1 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-4011.67fb1692.js differ diff --git a/priv/static/adminfe/static/js/chunk-4011.67fb1692.js.map b/priv/static/adminfe/static/js/chunk-4011.67fb1692.js.map new file mode 100644 index 000000000..6df398cbc Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-4011.67fb1692.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-46ef.671cac7d.js b/priv/static/adminfe/static/js/chunk-46ef.671cac7d.js deleted file mode 100644 index 805cdea13..000000000 Binary files a/priv/static/adminfe/static/js/chunk-46ef.671cac7d.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-46ef.671cac7d.js.map b/priv/static/adminfe/static/js/chunk-46ef.671cac7d.js.map deleted file mode 100644 index f6b420bb2..000000000 Binary files a/priv/static/adminfe/static/js/chunk-46ef.671cac7d.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-4ffb.0e8f3772.js b/priv/static/adminfe/static/js/chunk-4ffb.0e8f3772.js deleted file mode 100644 index 5a7aa9f59..000000000 Binary files a/priv/static/adminfe/static/js/chunk-4ffb.0e8f3772.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-4ffb.0e8f3772.js.map b/priv/static/adminfe/static/js/chunk-4ffb.0e8f3772.js.map deleted file mode 100644 index 7c020768c..000000000 Binary files a/priv/static/adminfe/static/js/chunk-4ffb.0e8f3772.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-6b68.fbc0f684.js b/priv/static/adminfe/static/js/chunk-6b68.fbc0f684.js new file mode 100644 index 000000000..bfdf936f8 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-6b68.fbc0f684.js differ diff --git a/priv/static/adminfe/static/js/chunk-6b68.fbc0f684.js.map b/priv/static/adminfe/static/js/chunk-6b68.fbc0f684.js.map new file mode 100644 index 000000000..d1d728b80 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-6b68.fbc0f684.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-6e81.3733ace2.js b/priv/static/adminfe/static/js/chunk-6e81.3733ace2.js new file mode 100644 index 000000000..c888ce03f Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-6e81.3733ace2.js differ diff --git a/priv/static/adminfe/static/js/chunk-6e81.3733ace2.js.map b/priv/static/adminfe/static/js/chunk-6e81.3733ace2.js.map new file mode 100644 index 000000000..63128dd67 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-6e81.3733ace2.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-7637.8f5fb36e.js b/priv/static/adminfe/static/js/chunk-7637.8f5fb36e.js new file mode 100644 index 000000000..b38644b98 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7637.8f5fb36e.js differ diff --git a/priv/static/adminfe/static/js/chunk-7637.8f5fb36e.js.map b/priv/static/adminfe/static/js/chunk-7637.8f5fb36e.js.map new file mode 100644 index 000000000..ddd53f1cd Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7637.8f5fb36e.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-876c.e4ceccca.js b/priv/static/adminfe/static/js/chunk-876c.e4ceccca.js deleted file mode 100644 index 841ceb9dc..000000000 Binary files a/priv/static/adminfe/static/js/chunk-876c.e4ceccca.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-876c.e4ceccca.js.map b/priv/static/adminfe/static/js/chunk-876c.e4ceccca.js.map deleted file mode 100644 index 88976a4fe..000000000 Binary files a/priv/static/adminfe/static/js/chunk-876c.e4ceccca.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-87b3.3c11ef09.js b/priv/static/adminfe/static/js/chunk-87b3.3c11ef09.js deleted file mode 100644 index 3899ff190..000000000 Binary files a/priv/static/adminfe/static/js/chunk-87b3.3c11ef09.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-87b3.3c11ef09.js.map b/priv/static/adminfe/static/js/chunk-87b3.3c11ef09.js.map deleted file mode 100644 index 6c6a85667..000000000 Binary files a/priv/static/adminfe/static/js/chunk-87b3.3c11ef09.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-88c9.e3583744.js b/priv/static/adminfe/static/js/chunk-88c9.e3583744.js deleted file mode 100644 index 0070fc30a..000000000 Binary files a/priv/static/adminfe/static/js/chunk-88c9.e3583744.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-88c9.e3583744.js.map b/priv/static/adminfe/static/js/chunk-88c9.e3583744.js.map deleted file mode 100644 index 20e503d0c..000000000 Binary files a/priv/static/adminfe/static/js/chunk-88c9.e3583744.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-970d.2457e066.js b/priv/static/adminfe/static/js/chunk-970d.2457e066.js new file mode 100644 index 000000000..0f99d835e Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-970d.2457e066.js differ diff --git a/priv/static/adminfe/static/js/chunk-970d.2457e066.js.map b/priv/static/adminfe/static/js/chunk-970d.2457e066.js.map new file mode 100644 index 000000000..6896407b0 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-970d.2457e066.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-cf57.3e45f57f.js b/priv/static/adminfe/static/js/chunk-cf57.3e45f57f.js deleted file mode 100644 index 2b4fd918f..000000000 Binary files a/priv/static/adminfe/static/js/chunk-cf57.3e45f57f.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-cf57.3e45f57f.js.map b/priv/static/adminfe/static/js/chunk-cf57.3e45f57f.js.map deleted file mode 100644 index 6457630bd..000000000 Binary files a/priv/static/adminfe/static/js/chunk-cf57.3e45f57f.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-d38a.a851004a.js b/priv/static/adminfe/static/js/chunk-d38a.a851004a.js new file mode 100644 index 000000000..c302af310 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-d38a.a851004a.js differ diff --git a/priv/static/adminfe/static/js/chunk-d38a.a851004a.js.map b/priv/static/adminfe/static/js/chunk-d38a.a851004a.js.map new file mode 100644 index 000000000..6779f6dc1 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-d38a.a851004a.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-e458.4e5aad44.js b/priv/static/adminfe/static/js/chunk-e458.4e5aad44.js new file mode 100644 index 000000000..a02c83110 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-e458.4e5aad44.js differ diff --git a/priv/static/adminfe/static/js/chunk-e458.4e5aad44.js.map b/priv/static/adminfe/static/js/chunk-e458.4e5aad44.js.map new file mode 100644 index 000000000..e623af23d Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-e458.4e5aad44.js.map differ diff --git a/priv/static/adminfe/static/js/runtime.1b4f6ce0.js b/priv/static/adminfe/static/js/runtime.1b4f6ce0.js new file mode 100644 index 000000000..6558531ba Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.1b4f6ce0.js differ diff --git a/priv/static/adminfe/static/js/runtime.1b4f6ce0.js.map b/priv/static/adminfe/static/js/runtime.1b4f6ce0.js.map new file mode 100644 index 000000000..9295ac636 Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.1b4f6ce0.js.map differ diff --git a/priv/static/adminfe/static/js/runtime.cb26bbd1.js b/priv/static/adminfe/static/js/runtime.cb26bbd1.js deleted file mode 100644 index 7180cc6e3..000000000 Binary files a/priv/static/adminfe/static/js/runtime.cb26bbd1.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.cb26bbd1.js.map b/priv/static/adminfe/static/js/runtime.cb26bbd1.js.map deleted file mode 100644 index 631198682..000000000 Binary files a/priv/static/adminfe/static/js/runtime.cb26bbd1.js.map and /dev/null differ -- cgit v1.2.3 From b6ca8cc53915b5cd86513b8c5214477626e97f3d Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 1 May 2020 16:48:31 +0000 Subject: Merge branch 'bugfix/1727-fix-signature-decoding' into 'develop' Bugfix/1727 fix signature decoding Closes #1727 See merge request pleroma/pleroma!2454 --- lib/pleroma/plugs/mapped_signature_to_identity_plug.ex | 5 +++-- lib/pleroma/signature.ex | 18 +++++++++++++++--- test/signature_test.exs | 18 ++++++++++++++---- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/pleroma/plugs/mapped_signature_to_identity_plug.ex b/lib/pleroma/plugs/mapped_signature_to_identity_plug.ex index 4f124ed4d..4cc93adb0 100644 --- a/lib/pleroma/plugs/mapped_signature_to_identity_plug.ex +++ b/lib/pleroma/plugs/mapped_signature_to_identity_plug.ex @@ -13,8 +13,9 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do def init(options), do: options defp key_id_from_conn(conn) do - with %{"keyId" => key_id} <- HTTPSignatures.signature_for_conn(conn) do - Signature.key_id_to_actor_id(key_id) + with %{"keyId" => key_id} <- HTTPSignatures.signature_for_conn(conn), + {:ok, ap_id} <- Signature.key_id_to_actor_id(key_id) do + ap_id else _ -> nil diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index 6b0b2c969..d01728361 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Signature do alias Pleroma.Keys alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.ObjectValidators.Types def key_id_to_actor_id(key_id) do uri = @@ -21,12 +22,23 @@ def key_id_to_actor_id(key_id) do uri end - URI.to_string(uri) + maybe_ap_id = URI.to_string(uri) + + case Types.ObjectID.cast(maybe_ap_id) do + {:ok, ap_id} -> + {:ok, ap_id} + + _ -> + case Pleroma.Web.WebFinger.finger(maybe_ap_id) do + %{"ap_id" => ap_id} -> {:ok, ap_id} + _ -> {:error, maybe_ap_id} + end + end end def fetch_public_key(conn) do with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn), - actor_id <- key_id_to_actor_id(kid), + {:ok, actor_id} <- key_id_to_actor_id(kid), {:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do {:ok, public_key} else @@ -37,7 +49,7 @@ def fetch_public_key(conn) do def refetch_public_key(conn) do with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn), - actor_id <- key_id_to_actor_id(kid), + {:ok, actor_id} <- key_id_to_actor_id(kid), {:ok, _user} <- ActivityPub.make_user_from_ap_id(actor_id), {:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do {:ok, public_key} diff --git a/test/signature_test.exs b/test/signature_test.exs index 04736d8b9..f3bba1378 100644 --- a/test/signature_test.exs +++ b/test/signature_test.exs @@ -49,7 +49,8 @@ test "it returns key" do test "it returns error when not found user" do assert capture_log(fn -> - assert Signature.fetch_public_key(make_fake_conn("test-ap_id")) == {:error, :error} + assert Signature.fetch_public_key(make_fake_conn("https://test-ap-id")) == + {:error, :error} end) =~ "[error] Could not decode user" end @@ -69,7 +70,7 @@ test "it returns key" do test "it returns error when not found user" do assert capture_log(fn -> - {:error, _} = Signature.refetch_public_key(make_fake_conn("test-ap_id")) + {:error, _} = Signature.refetch_public_key(make_fake_conn("https://test-ap_id")) end) =~ "[error] Could not decode user" end end @@ -105,12 +106,21 @@ test "it returns error" do describe "key_id_to_actor_id/1" do test "it properly deduces the actor id for misskey" do assert Signature.key_id_to_actor_id("https://example.com/users/1234/publickey") == - "https://example.com/users/1234" + {:ok, "https://example.com/users/1234"} end test "it properly deduces the actor id for mastodon and pleroma" do assert Signature.key_id_to_actor_id("https://example.com/users/1234#main-key") == - "https://example.com/users/1234" + {:ok, "https://example.com/users/1234"} + end + + test "it calls webfinger for 'acct:' accounts" do + with_mock(Pleroma.Web.WebFinger, + finger: fn _ -> %{"ap_id" => "https://gensokyo.2hu/users/raymoo"} end + ) do + assert Signature.key_id_to_actor_id("acct:raymoo@gensokyo.2hu") == + {:ok, "https://gensokyo.2hu/users/raymoo"} + end end end -- cgit v1.2.3 From 2646fdea224cb2af7d2adc66265c334ae1c69669 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 1 May 2020 22:29:22 +0300 Subject: signature.ex: Remove the use of ObjectID type It is not in stable yet --- lib/pleroma/signature.ex | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index d01728361..7006eb2c0 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -8,7 +8,6 @@ defmodule Pleroma.Signature do alias Pleroma.Keys alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.ObjectValidators.Types def key_id_to_actor_id(key_id) do uri = @@ -22,16 +21,14 @@ def key_id_to_actor_id(key_id) do uri end - maybe_ap_id = URI.to_string(uri) - - case Types.ObjectID.cast(maybe_ap_id) do - {:ok, ap_id} -> - {:ok, ap_id} + case uri do + %URI{scheme: scheme} when scheme in ["https", "http"] -> + {:ok, URI.to_string(uri)} _ -> - case Pleroma.Web.WebFinger.finger(maybe_ap_id) do + case Pleroma.Web.WebFinger.finger(URI.to_string(uri)) do %{"ap_id" => ap_id} -> {:ok, ap_id} - _ -> {:error, maybe_ap_id} + _ -> {:error, URI.to_string(uri)} end end end -- cgit v1.2.3 From f08c8d99324b6b557521d055e6e6ce44f6d0fccf Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 2 May 2020 18:06:29 +0300 Subject: pleroma-fe bundle: update to a0f780c4550b77d4574e0de8932a2dff288784a3 --- priv/static/index.html | 2 +- priv/static/static/font/fontello.1586352043988.eot | Bin 22444 -> 0 bytes priv/static/static/font/fontello.1586352043988.svg | 118 -------------------- priv/static/static/font/fontello.1586352043988.ttf | Bin 22276 -> 0 bytes .../static/static/font/fontello.1586352043988.woff | Bin 13656 -> 0 bytes .../static/font/fontello.1586352043988.woff2 | Bin 11532 -> 0 bytes priv/static/static/font/fontello.1588431888583.eot | Bin 0 -> 22752 bytes priv/static/static/font/fontello.1588431888583.svg | 122 +++++++++++++++++++++ priv/static/static/font/fontello.1588431888583.ttf | Bin 0 -> 22584 bytes .../static/static/font/fontello.1588431888583.woff | Bin 0 -> 13836 bytes .../static/font/fontello.1588431888583.woff2 | Bin 0 -> 11668 bytes priv/static/static/fontello.1583594169021.css | Bin 3296 -> 0 bytes priv/static/static/fontello.1586352043988.css | Bin 3296 -> 0 bytes priv/static/static/fontello.1588431888583.css | Bin 0 -> 3378 bytes priv/static/static/fontello.json | 14 ++- priv/static/static/js/2.93c984e8c993f92c77a1.js | Bin 0 -> 2169 bytes .../static/static/js/2.93c984e8c993f92c77a1.js.map | Bin 0 -> 7927 bytes priv/static/static/js/2.f158cbd2b8770e467dfe.js | Bin 2169 -> 0 bytes .../static/static/js/2.f158cbd2b8770e467dfe.js.map | Bin 7927 -> 0 bytes priv/static/static/js/app.57951e6e5e198d1a1266.js | Bin 0 -> 1050038 bytes .../static/js/app.57951e6e5e198d1a1266.js.map | Bin 0 -> 1669149 bytes priv/static/static/js/app.89eafa17d89159680407.js | Bin 1045613 -> 0 bytes .../static/js/app.89eafa17d89159680407.js.map | Bin 1658861 -> 0 bytes .../static/js/vendors~app.c67e1a363ece7f1f7152.js | Bin 0 -> 377394 bytes .../js/vendors~app.c67e1a363ece7f1f7152.js.map | Bin 0 -> 1737991 bytes .../static/js/vendors~app.de343579e844e698d456.js | Bin 377393 -> 0 bytes .../js/vendors~app.de343579e844e698d456.js.map | Bin 1737985 -> 0 bytes priv/static/sw-pleroma.js | Bin 31329 -> 31329 bytes 28 files changed, 136 insertions(+), 120 deletions(-) delete mode 100644 priv/static/static/font/fontello.1586352043988.eot delete mode 100644 priv/static/static/font/fontello.1586352043988.svg delete mode 100644 priv/static/static/font/fontello.1586352043988.ttf delete mode 100644 priv/static/static/font/fontello.1586352043988.woff delete mode 100644 priv/static/static/font/fontello.1586352043988.woff2 create mode 100644 priv/static/static/font/fontello.1588431888583.eot create mode 100644 priv/static/static/font/fontello.1588431888583.svg create mode 100644 priv/static/static/font/fontello.1588431888583.ttf create mode 100644 priv/static/static/font/fontello.1588431888583.woff create mode 100644 priv/static/static/font/fontello.1588431888583.woff2 delete mode 100644 priv/static/static/fontello.1583594169021.css delete mode 100644 priv/static/static/fontello.1586352043988.css create mode 100644 priv/static/static/fontello.1588431888583.css create mode 100644 priv/static/static/js/2.93c984e8c993f92c77a1.js create mode 100644 priv/static/static/js/2.93c984e8c993f92c77a1.js.map delete mode 100644 priv/static/static/js/2.f158cbd2b8770e467dfe.js delete mode 100644 priv/static/static/js/2.f158cbd2b8770e467dfe.js.map create mode 100644 priv/static/static/js/app.57951e6e5e198d1a1266.js create mode 100644 priv/static/static/js/app.57951e6e5e198d1a1266.js.map delete mode 100644 priv/static/static/js/app.89eafa17d89159680407.js delete mode 100644 priv/static/static/js/app.89eafa17d89159680407.js.map create mode 100644 priv/static/static/js/vendors~app.c67e1a363ece7f1f7152.js create mode 100644 priv/static/static/js/vendors~app.c67e1a363ece7f1f7152.js.map delete mode 100644 priv/static/static/js/vendors~app.de343579e844e698d456.js delete mode 100644 priv/static/static/js/vendors~app.de343579e844e698d456.js.map diff --git a/priv/static/index.html b/priv/static/index.html index 08813ac8f..2958cda1b 100644 --- a/priv/static/index.html +++ b/priv/static/index.html @@ -1 +1 @@ -Pleroma
\ No newline at end of file +Pleroma
\ No newline at end of file diff --git a/priv/static/static/font/fontello.1586352043988.eot b/priv/static/static/font/fontello.1586352043988.eot deleted file mode 100644 index 66e87206e..000000000 Binary files a/priv/static/static/font/fontello.1586352043988.eot and /dev/null differ diff --git a/priv/static/static/font/fontello.1586352043988.svg b/priv/static/static/font/fontello.1586352043988.svg deleted file mode 100644 index b905a0f6c..000000000 --- a/priv/static/static/font/fontello.1586352043988.svg +++ /dev/null @@ -1,118 +0,0 @@ - - - -Copyright (C) 2020 by original authors @ fontello.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/priv/static/static/font/fontello.1586352043988.ttf b/priv/static/static/font/fontello.1586352043988.ttf deleted file mode 100644 index 816c2a519..000000000 Binary files a/priv/static/static/font/fontello.1586352043988.ttf and /dev/null differ diff --git a/priv/static/static/font/fontello.1586352043988.woff b/priv/static/static/font/fontello.1586352043988.woff deleted file mode 100644 index 6f28b1225..000000000 Binary files a/priv/static/static/font/fontello.1586352043988.woff and /dev/null differ diff --git a/priv/static/static/font/fontello.1586352043988.woff2 b/priv/static/static/font/fontello.1586352043988.woff2 deleted file mode 100644 index 3d225aba5..000000000 Binary files a/priv/static/static/font/fontello.1586352043988.woff2 and /dev/null differ diff --git a/priv/static/static/font/fontello.1588431888583.eot b/priv/static/static/font/fontello.1588431888583.eot new file mode 100644 index 000000000..1f2934b93 Binary files /dev/null and b/priv/static/static/font/fontello.1588431888583.eot differ diff --git a/priv/static/static/font/fontello.1588431888583.svg b/priv/static/static/font/fontello.1588431888583.svg new file mode 100644 index 000000000..71f81f435 --- /dev/null +++ b/priv/static/static/font/fontello.1588431888583.svg @@ -0,0 +1,122 @@ + + + +Copyright (C) 2020 by original authors @ fontello.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/priv/static/static/font/fontello.1588431888583.ttf b/priv/static/static/font/fontello.1588431888583.ttf new file mode 100644 index 000000000..b4fb51df0 Binary files /dev/null and b/priv/static/static/font/fontello.1588431888583.ttf differ diff --git a/priv/static/static/font/fontello.1588431888583.woff b/priv/static/static/font/fontello.1588431888583.woff new file mode 100644 index 000000000..a50623bbb Binary files /dev/null and b/priv/static/static/font/fontello.1588431888583.woff differ diff --git a/priv/static/static/font/fontello.1588431888583.woff2 b/priv/static/static/font/fontello.1588431888583.woff2 new file mode 100644 index 000000000..150c93c1a Binary files /dev/null and b/priv/static/static/font/fontello.1588431888583.woff2 differ diff --git a/priv/static/static/fontello.1583594169021.css b/priv/static/static/fontello.1583594169021.css deleted file mode 100644 index c096e6103..000000000 Binary files a/priv/static/static/fontello.1583594169021.css and /dev/null differ diff --git a/priv/static/static/fontello.1586352043988.css b/priv/static/static/fontello.1586352043988.css deleted file mode 100644 index 9f345bdae..000000000 Binary files a/priv/static/static/fontello.1586352043988.css and /dev/null differ diff --git a/priv/static/static/fontello.1588431888583.css b/priv/static/static/fontello.1588431888583.css new file mode 100644 index 000000000..cb9ad1025 Binary files /dev/null and b/priv/static/static/fontello.1588431888583.css differ diff --git a/priv/static/static/fontello.json b/priv/static/static/fontello.json index 5a7086a23..5963b68b4 100755 --- a/priv/static/static/fontello.json +++ b/priv/static/static/fontello.json @@ -345,6 +345,18 @@ "css": "link", "code": 59427, "src": "fontawesome" + }, + { + "uid": "8b80d36d4ef43889db10bc1f0dc9a862", + "css": "user", + "code": 59428, + "src": "fontawesome" + }, + { + "uid": "12f4ece88e46abd864e40b35e05b11cd", + "css": "ok", + "code": 59431, + "src": "fontawesome" } ] -} +} \ No newline at end of file diff --git a/priv/static/static/js/2.93c984e8c993f92c77a1.js b/priv/static/static/js/2.93c984e8c993f92c77a1.js new file mode 100644 index 000000000..4faa3d66f Binary files /dev/null and b/priv/static/static/js/2.93c984e8c993f92c77a1.js differ diff --git a/priv/static/static/js/2.93c984e8c993f92c77a1.js.map b/priv/static/static/js/2.93c984e8c993f92c77a1.js.map new file mode 100644 index 000000000..12e4d0c12 Binary files /dev/null and b/priv/static/static/js/2.93c984e8c993f92c77a1.js.map differ diff --git a/priv/static/static/js/2.f158cbd2b8770e467dfe.js b/priv/static/static/js/2.f158cbd2b8770e467dfe.js deleted file mode 100644 index 24f80fe7b..000000000 Binary files a/priv/static/static/js/2.f158cbd2b8770e467dfe.js and /dev/null differ diff --git a/priv/static/static/js/2.f158cbd2b8770e467dfe.js.map b/priv/static/static/js/2.f158cbd2b8770e467dfe.js.map deleted file mode 100644 index 94ca6f090..000000000 Binary files a/priv/static/static/js/2.f158cbd2b8770e467dfe.js.map and /dev/null differ diff --git a/priv/static/static/js/app.57951e6e5e198d1a1266.js b/priv/static/static/js/app.57951e6e5e198d1a1266.js new file mode 100644 index 000000000..f68e594d9 Binary files /dev/null and b/priv/static/static/js/app.57951e6e5e198d1a1266.js differ diff --git a/priv/static/static/js/app.57951e6e5e198d1a1266.js.map b/priv/static/static/js/app.57951e6e5e198d1a1266.js.map new file mode 100644 index 000000000..cb97ac977 Binary files /dev/null and b/priv/static/static/js/app.57951e6e5e198d1a1266.js.map differ diff --git a/priv/static/static/js/app.89eafa17d89159680407.js b/priv/static/static/js/app.89eafa17d89159680407.js deleted file mode 100644 index 6ab20dd4e..000000000 Binary files a/priv/static/static/js/app.89eafa17d89159680407.js and /dev/null differ diff --git a/priv/static/static/js/app.89eafa17d89159680407.js.map b/priv/static/static/js/app.89eafa17d89159680407.js.map deleted file mode 100644 index 1178b5790..000000000 Binary files a/priv/static/static/js/app.89eafa17d89159680407.js.map and /dev/null differ diff --git a/priv/static/static/js/vendors~app.c67e1a363ece7f1f7152.js b/priv/static/static/js/vendors~app.c67e1a363ece7f1f7152.js new file mode 100644 index 000000000..2a98ce02b Binary files /dev/null and b/priv/static/static/js/vendors~app.c67e1a363ece7f1f7152.js differ diff --git a/priv/static/static/js/vendors~app.c67e1a363ece7f1f7152.js.map b/priv/static/static/js/vendors~app.c67e1a363ece7f1f7152.js.map new file mode 100644 index 000000000..c32cda3bd Binary files /dev/null and b/priv/static/static/js/vendors~app.c67e1a363ece7f1f7152.js.map differ diff --git a/priv/static/static/js/vendors~app.de343579e844e698d456.js b/priv/static/static/js/vendors~app.de343579e844e698d456.js deleted file mode 100644 index d5c844ba9..000000000 Binary files a/priv/static/static/js/vendors~app.de343579e844e698d456.js and /dev/null differ diff --git a/priv/static/static/js/vendors~app.de343579e844e698d456.js.map b/priv/static/static/js/vendors~app.de343579e844e698d456.js.map deleted file mode 100644 index a56756cfe..000000000 Binary files a/priv/static/static/js/vendors~app.de343579e844e698d456.js.map and /dev/null differ diff --git a/priv/static/sw-pleroma.js b/priv/static/sw-pleroma.js index 7cfcfefd0..362b6dba5 100644 Binary files a/priv/static/sw-pleroma.js and b/priv/static/sw-pleroma.js differ -- cgit v1.2.3 From 7b0593367756adc459a7b7bbaa0ace567b97742f Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sun, 19 Apr 2020 21:45:20 +0000 Subject: Merge branch '1559-follow-request-notifications' into 'develop' [#1559] Support for "follow_request" notifications Closes #1559 See merge request pleroma/pleroma!2354 --- config/config.exs | 2 + config/description.exs | 14 ++++ lib/pleroma/activity.ex | 38 ++++++++-- lib/pleroma/notification.ex | 11 ++- lib/pleroma/user.ex | 2 + .../web/mastodon_api/views/notification_view.ex | 6 +- lib/pleroma/web/push/impl.ex | 77 +++++++++++++-------- lib/pleroma/web/push/subscription.ex | 8 +++ test/notification_test.exs | 80 +++++++++++++++++++--- 9 files changed, 189 insertions(+), 49 deletions(-) diff --git a/config/config.exs b/config/config.exs index 3357e23e7..6b881e1b2 100644 --- a/config/config.exs +++ b/config/config.exs @@ -581,6 +581,8 @@ inactivity_threshold: 7 } +config :pleroma, :notifications, enable_follow_request_notifications: false + config :pleroma, :oauth2, token_expires_in: 600, issue_new_refresh_token: true, diff --git a/config/description.exs b/config/description.exs index 5a1e9e9af..19ff55540 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2286,6 +2286,20 @@ } ] }, + %{ + group: :pleroma, + key: :notifications, + type: :group, + description: "Notification settings", + children: [ + %{ + key: :enable_follow_request_notifications, + type: :boolean, + description: + "Enables notifications on new follow requests (causes issues with older PleromaFE versions)." + } + ] + }, %{ group: :pleroma, key: Pleroma.Emails.UserEmail, diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 5a8329e69..6213d0eb7 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -27,17 +27,13 @@ defmodule Pleroma.Activity do # https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19 @mastodon_notification_types %{ "Create" => "mention", - "Follow" => "follow", + "Follow" => ["follow", "follow_request"], "Announce" => "reblog", "Like" => "favourite", "Move" => "move", "EmojiReact" => "pleroma:emoji_reaction" } - @mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types, - into: %{}, - do: {v, k} - schema "activities" do field(:data, :map) field(:local, :boolean, default: true) @@ -291,15 +287,43 @@ defp purge_web_resp_cache(%Activity{} = activity) do defp purge_web_resp_cache(nil), do: nil - for {ap_type, type} <- @mastodon_notification_types do + def follow_accepted?( + %Activity{data: %{"type" => "Follow", "object" => followed_ap_id}} = activity + ) do + with %User{} = follower <- Activity.user_actor(activity), + %User{} = followed <- User.get_cached_by_ap_id(followed_ap_id) do + Pleroma.FollowingRelationship.following?(follower, followed) + else + _ -> false + end + end + + def follow_accepted?(_), do: false + + @spec mastodon_notification_type(Activity.t()) :: String.t() | nil + + for {ap_type, type} <- @mastodon_notification_types, not is_list(type) do def mastodon_notification_type(%Activity{data: %{"type" => unquote(ap_type)}}), do: unquote(type) end + def mastodon_notification_type(%Activity{data: %{"type" => "Follow"}} = activity) do + if follow_accepted?(activity) do + "follow" + else + "follow_request" + end + end + def mastodon_notification_type(%Activity{}), do: nil + @spec from_mastodon_notification_type(String.t()) :: String.t() | nil + @doc "Converts Mastodon notification type to AR activity type" def from_mastodon_notification_type(type) do - Map.get(@mastodon_to_ap_notification_types, type) + with {k, _v} <- + Enum.find(@mastodon_notification_types, fn {_k, v} -> type in List.wrap(v) end) do + k + end end def all_by_actor_and_id(actor, status_ids \\ []) diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 824ba5ecb..94dc0c2b0 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -293,8 +293,17 @@ def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = act end end + def create_notifications(%Activity{data: %{"type" => "Follow"}} = activity) do + if Pleroma.Config.get([:notifications, :enable_follow_request_notifications]) || + Activity.follow_accepted?(activity) do + do_create_notifications(activity) + else + {:ok, []} + end + end + def create_notifications(%Activity{data: %{"type" => type}} = activity) - when type in ["Like", "Announce", "Follow", "Move", "EmojiReact"] do + when type in ["Like", "Announce", "Move", "EmojiReact"] do do_create_notifications(activity) end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index df96f38df..2f0333da0 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -706,6 +706,8 @@ def needs_update?(%User{local: false} = user) do def needs_update?(_), do: true @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()} + + # "Locked" (self-locked) users demand explicit authorization of follow requests def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do follow(follower, followed, :follow_pending) end diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index 33145c484..1720fbead 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -49,12 +49,12 @@ def render("show.json", %{ "move" -> put_target(response, activity, user) - "follow" -> - response - "pleroma:emoji_reaction" -> put_status(response, parent_activity, user) |> put_emoji(activity) + type when type in ["follow", "follow_request"] -> + response + _ -> nil end diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index afa510f08..f1740a6e0 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -16,6 +16,8 @@ defmodule Pleroma.Web.Push.Impl do require Logger import Ecto.Query + defdelegate mastodon_notification_type(activity), to: Activity + @types ["Create", "Follow", "Announce", "Like", "Move"] @doc "Performs sending notifications for user subscriptions" @@ -24,32 +26,32 @@ def perform( %{ activity: %{data: %{"type" => activity_type}} = activity, user: %User{id: user_id} - } = notif + } = notification ) when activity_type in @types do - actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) + actor = User.get_cached_by_ap_id(notification.activity.data["actor"]) - type = Activity.mastodon_notification_type(notif.activity) + mastodon_type = mastodon_notification_type(notification.activity) gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key) avatar_url = User.avatar_url(actor) object = Object.normalize(activity) user = User.get_cached_by_id(user_id) direct_conversation_id = Activity.direct_conversation_id(activity, user) - for subscription <- fetch_subsriptions(user_id), - get_in(subscription.data, ["alerts", type]) do + for subscription <- fetch_subscriptions(user_id), + Subscription.enabled?(subscription, mastodon_type) do %{ access_token: subscription.token.token, - notification_id: notif.id, - notification_type: type, + notification_id: notification.id, + notification_type: mastodon_type, icon: avatar_url, preferred_locale: "en", pleroma: %{ - activity_id: notif.activity.id, + activity_id: notification.activity.id, direct_conversation_id: direct_conversation_id } } - |> Map.merge(build_content(notif, actor, object)) + |> Map.merge(build_content(notification, actor, object, mastodon_type)) |> Jason.encode!() |> push_message(build_sub(subscription), gcm_api_key, subscription) end @@ -82,7 +84,7 @@ def push_message(body, sub, api_key, subscription) do end @doc "Gets user subscriptions" - def fetch_subsriptions(user_id) do + def fetch_subscriptions(user_id) do Subscription |> where(user_id: ^user_id) |> preload(:token) @@ -99,28 +101,36 @@ def build_sub(subscription) do } end + def build_content(notification, actor, object, mastodon_type \\ nil) + def build_content( %{ activity: %{data: %{"directMessage" => true}}, user: %{notification_settings: %{privacy_option: true}} }, actor, - _ + _object, + _mastodon_type ) do %{title: "New Direct Message", body: "@#{actor.nickname}"} end - def build_content(notif, actor, object) do + def build_content(notification, actor, object, mastodon_type) do + mastodon_type = mastodon_type || mastodon_notification_type(notification.activity) + %{ - title: format_title(notif), - body: format_body(notif, actor, object) + title: format_title(notification, mastodon_type), + body: format_body(notification, actor, object, mastodon_type) } end + def format_body(activity, actor, object, mastodon_type \\ nil) + def format_body( %{activity: %{data: %{"type" => "Create"}}}, actor, - %{data: %{"content" => content}} + %{data: %{"content" => content}}, + _mastodon_type ) do "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" end @@ -128,33 +138,44 @@ def format_body( def format_body( %{activity: %{data: %{"type" => "Announce"}}}, actor, - %{data: %{"content" => content}} + %{data: %{"content" => content}}, + _mastodon_type ) do "@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}" end def format_body( - %{activity: %{data: %{"type" => type}}}, + %{activity: %{data: %{"type" => type}}} = notification, actor, - _object + _object, + mastodon_type ) when type in ["Follow", "Like"] do - case type do - "Follow" -> "@#{actor.nickname} has followed you" - "Like" -> "@#{actor.nickname} has favorited your post" + mastodon_type = mastodon_type || mastodon_notification_type(notification.activity) + + case mastodon_type do + "follow" -> "@#{actor.nickname} has followed you" + "follow_request" -> "@#{actor.nickname} has requested to follow you" + "favourite" -> "@#{actor.nickname} has favorited your post" end end - def format_title(%{activity: %{data: %{"directMessage" => true}}}) do + def format_title(activity, mastodon_type \\ nil) + + def format_title(%{activity: %{data: %{"directMessage" => true}}}, _mastodon_type) do "New Direct Message" end - def format_title(%{activity: %{data: %{"type" => type}}}) do - case type do - "Create" -> "New Mention" - "Follow" -> "New Follower" - "Announce" -> "New Repeat" - "Like" -> "New Favorite" + def format_title(%{activity: activity}, mastodon_type) do + mastodon_type = mastodon_type || mastodon_notification_type(activity) + + case mastodon_type do + "mention" -> "New Mention" + "follow" -> "New Follower" + "follow_request" -> "New Follow Request" + "reblog" -> "New Repeat" + "favourite" -> "New Favorite" + type -> "New #{String.capitalize(type || "event")}" end end end diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex index 5c448d6c9..b99b0c5fb 100644 --- a/lib/pleroma/web/push/subscription.ex +++ b/lib/pleroma/web/push/subscription.ex @@ -32,6 +32,14 @@ defp alerts(%{"data" => %{"alerts" => alerts}}) do %{"alerts" => alerts} end + def enabled?(subscription, "follow_request") do + enabled?(subscription, "follow") + end + + def enabled?(subscription, alert_type) do + get_in(subscription.data, ["alerts", alert_type]) + end + def create( %User{} = user, %Token{} = token, diff --git a/test/notification_test.exs b/test/notification_test.exs index a7282c929..8553d9652 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -8,11 +8,13 @@ defmodule Pleroma.NotificationTest do import Pleroma.Factory import Mock + alias Pleroma.FollowingRelationship alias Pleroma.Notification alias Pleroma.Tests.ObanHelpers alias Pleroma.User alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.CommonAPI + alias Pleroma.Web.MastodonAPI.NotificationView alias Pleroma.Web.Push alias Pleroma.Web.Streamer @@ -272,16 +274,6 @@ test "it doesn't create a notification for user if he is the activity author" do refute Notification.create_notification(activity, author) end - test "it doesn't create a notification for follow-unfollow-follow chains" do - user = insert(:user) - followed_user = insert(:user) - {:ok, _, _, activity} = CommonAPI.follow(user, followed_user) - Notification.create_notification(activity, followed_user) - CommonAPI.unfollow(user, followed_user) - {:ok, _, _, activity_dupe} = CommonAPI.follow(user, followed_user) - refute Notification.create_notification(activity_dupe, followed_user) - end - test "it doesn't create duplicate notifications for follow+subscribed users" do user = insert(:user) subscriber = insert(:user) @@ -304,6 +296,74 @@ test "it doesn't create subscription notifications if the recipient cannot see t end end + describe "follow / follow_request notifications" do + test "it creates `follow` notification for approved Follow activity" do + user = insert(:user) + followed_user = insert(:user, locked: false) + + {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) + assert FollowingRelationship.following?(user, followed_user) + assert [notification] = Notification.for_user(followed_user) + + assert %{type: "follow"} = + NotificationView.render("show.json", %{ + notification: notification, + for: followed_user + }) + end + + test "if `follow_request` notifications are enabled, " <> + "it creates `follow_request` notification for pending Follow activity" do + clear_config([:notifications, :enable_follow_request_notifications], true) + user = insert(:user) + followed_user = insert(:user, locked: true) + + {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) + refute FollowingRelationship.following?(user, followed_user) + assert [notification] = Notification.for_user(followed_user) + + render_opts = %{notification: notification, for: followed_user} + assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts) + + # After request is accepted, the same notification is rendered with type "follow": + assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user) + + notification_id = notification.id + assert [%{id: ^notification_id}] = Notification.for_user(followed_user) + assert %{type: "follow"} = NotificationView.render("show.json", render_opts) + end + + test "if `follow_request` notifications are disabled, " <> + "it does NOT create `follow*` notification for pending Follow activity" do + clear_config([:notifications, :enable_follow_request_notifications], false) + user = insert(:user) + followed_user = insert(:user, locked: true) + + {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) + refute FollowingRelationship.following?(user, followed_user) + assert [] = Notification.for_user(followed_user) + + # After request is accepted, no new notifications are generated: + assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user) + assert [] = Notification.for_user(followed_user) + end + + test "it doesn't create a notification for follow-unfollow-follow chains" do + user = insert(:user) + followed_user = insert(:user, locked: false) + + {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) + assert FollowingRelationship.following?(user, followed_user) + assert [notification] = Notification.for_user(followed_user) + + CommonAPI.unfollow(user, followed_user) + {:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user) + + notification_id = notification.id + assert [%{id: ^notification_id}] = Notification.for_user(followed_user) + end + end + describe "get notification" do test "it gets a notification that belongs to the user" do user = insert(:user) -- cgit v1.2.3 From c75840f7b8a117e973713c8cb47523f09deaab79 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 2 May 2020 12:24:49 +0000 Subject: Merge branch 'follow-request-notifications' into 'develop' Follow request notifications enforcement See merge request pleroma/pleroma!2451 --- config/config.exs | 2 -- config/description.exs | 14 -------------- lib/pleroma/notification.ex | 11 +---------- test/notification_test.exs | 28 ++++++++++------------------ 4 files changed, 11 insertions(+), 44 deletions(-) diff --git a/config/config.exs b/config/config.exs index 6b881e1b2..3357e23e7 100644 --- a/config/config.exs +++ b/config/config.exs @@ -581,8 +581,6 @@ inactivity_threshold: 7 } -config :pleroma, :notifications, enable_follow_request_notifications: false - config :pleroma, :oauth2, token_expires_in: 600, issue_new_refresh_token: true, diff --git a/config/description.exs b/config/description.exs index 19ff55540..5a1e9e9af 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2286,20 +2286,6 @@ } ] }, - %{ - group: :pleroma, - key: :notifications, - type: :group, - description: "Notification settings", - children: [ - %{ - key: :enable_follow_request_notifications, - type: :boolean, - description: - "Enables notifications on new follow requests (causes issues with older PleromaFE versions)." - } - ] - }, %{ group: :pleroma, key: Pleroma.Emails.UserEmail, diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 94dc0c2b0..815356a5e 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -293,17 +293,8 @@ def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = act end end - def create_notifications(%Activity{data: %{"type" => "Follow"}} = activity) do - if Pleroma.Config.get([:notifications, :enable_follow_request_notifications]) || - Activity.follow_accepted?(activity) do - do_create_notifications(activity) - else - {:ok, []} - end - end - def create_notifications(%Activity{data: %{"type" => type}} = activity) - when type in ["Like", "Announce", "Move", "EmojiReact"] do + when type in ["Follow", "Like", "Announce", "Move", "EmojiReact"] do do_create_notifications(activity) end diff --git a/test/notification_test.exs b/test/notification_test.exs index 8553d9652..e12418db3 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -312,9 +312,7 @@ test "it creates `follow` notification for approved Follow activity" do }) end - test "if `follow_request` notifications are enabled, " <> - "it creates `follow_request` notification for pending Follow activity" do - clear_config([:notifications, :enable_follow_request_notifications], true) + test "it creates `follow_request` notification for pending Follow activity" do user = insert(:user) followed_user = insert(:user, locked: true) @@ -333,21 +331,6 @@ test "if `follow_request` notifications are enabled, " <> assert %{type: "follow"} = NotificationView.render("show.json", render_opts) end - test "if `follow_request` notifications are disabled, " <> - "it does NOT create `follow*` notification for pending Follow activity" do - clear_config([:notifications, :enable_follow_request_notifications], false) - user = insert(:user) - followed_user = insert(:user, locked: true) - - {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) - refute FollowingRelationship.following?(user, followed_user) - assert [] = Notification.for_user(followed_user) - - # After request is accepted, no new notifications are generated: - assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user) - assert [] = Notification.for_user(followed_user) - end - test "it doesn't create a notification for follow-unfollow-follow chains" do user = insert(:user) followed_user = insert(:user, locked: false) @@ -362,6 +345,15 @@ test "it doesn't create a notification for follow-unfollow-follow chains" do notification_id = notification.id assert [%{id: ^notification_id}] = Notification.for_user(followed_user) end + + test "dismisses the notification on follow request rejection" do + user = insert(:user, locked: true) + follower = insert(:user) + {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user) + assert [notification] = Notification.for_user(user) + {:ok, _follower} = CommonAPI.reject_follow_request(follower, user) + assert [] = Notification.for_user(user) + end end describe "get notification" do -- cgit v1.2.3 From 441e28f36562492ad076443c4d061b33fc0f9813 Mon Sep 17 00:00:00 2001 From: eugenijm Date: Thu, 30 Apr 2020 15:02:35 +0300 Subject: Dismiss the follow request notification on rejection --- lib/pleroma/notification.ex | 10 ++++++++++ lib/pleroma/web/common_api/common_api.ex | 2 ++ 2 files changed, 12 insertions(+) diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 815356a5e..556075fba 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -271,6 +271,16 @@ def destroy_multiple(%{id: user_id} = _user, ids) do |> Repo.delete_all() end + def dismiss(%Pleroma.Activity{} = activity) do + Notification + |> where([n], n.activity_id == ^activity.id) + |> Repo.delete_all() + |> case do + {_, notifications} -> {:ok, notifications} + _ -> {:error, "Cannot dismiss notification"} + end + end + def dismiss(%{id: user_id} = _user, id) do notification = Repo.get(Notification, id) diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index c84782008..74e9e8cfa 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.CommonAPI do alias Pleroma.ActivityExpiration alias Pleroma.Conversation.Participation alias Pleroma.FollowingRelationship + alias Pleroma.Notification alias Pleroma.Object alias Pleroma.ThreadMute alias Pleroma.User @@ -58,6 +59,7 @@ def reject_follow_request(follower, followed) do with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject), + {:ok, _notifications} <- Notification.dismiss(follow_activity), {:ok, _activity} <- ActivityPub.reject(%{ to: [follower.ap_id], -- cgit v1.2.3 From e0d392c04c3ff6e1c2d777c40e3cf44dfa68a136 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 1 May 2020 22:10:02 +0300 Subject: CHANGELOG.md: Add 2.0.3 changelog --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c861699f4..f3394ecbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,38 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [2.0.3] - 2020-05-02 + +### Security +- Disallow re-registration of previously deleted users, which allowed viewing direct messages addressed to them +- Mastodon API: Fix `POST /api/v1/follow_requests/:id/authorize` allowing to force a follow from a local user even if they didn't request to follow +- CSP: Sandbox uploads + +### Fixed +- Notifications from blocked domains +- Potential federation issues with Mastodon versions before 3.0.0 +- HTTP Basic Authentication permissions issue +- Follow/Block imports not being able to find the user if the nickname started with an `@` +- Instance stats counting internal users +- Inability to run a From Source release without git +- ObjectAgePolicy didn't filter out old messages +- `blob:` urls not being allowed by CSP + +### Added +- NodeInfo: ObjectAgePolicy settings to the `federation` list. +- Follow request notifications +
+ API Changes +- Admin API: `GET /api/pleroma/admin/need_reboot`. +
+ +### Upgrade notes + +1. Restart Pleroma +2. Run database migrations (inside Pleroma directory): + - OTP: `./bin/pleroma_ctl migrate` + - From Source: `mix ecto.migrate` + ## [2.0.2] - 2020-04-08 ### Added - Support for Funkwhale's `Audio` activity -- cgit v1.2.3 From 04f23294d327f044a72ecd3f269846c2f6198cf1 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 1 May 2020 22:11:33 +0300 Subject: mix.exs: bump to 2.0.3 --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 571adc802..3f3990ea2 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do def project do [ app: :pleroma, - version: version("2.0.2"), + version: version("2.0.3"), elixir: "~> 1.8", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), -- cgit v1.2.3