Merge remote-tracking branch 'origin/develop' into notice-routes
authorAlex Gleason <alex@alexgleason.me>
Sun, 26 Dec 2021 01:57:53 +0000 (19:57 -0600)
committerAlex Gleason <alex@alexgleason.me>
Sun, 26 Dec 2021 01:57:53 +0000 (19:57 -0600)
1  2 
CHANGELOG.md
lib/pleroma/web/router.ex
lib/pleroma/web/static_fe/static_fe_controller.ex
test/pleroma/web/o_status/o_status_controller_test.exs
test/pleroma/web/plugs/frontend_static_plug_test.exs

diff --combined CHANGELOG.md
index 625cf3266fc09928de74eca08871aeca2f2d5df6,8e97da1896ab9467f0bf9637588821c2c6d1b536..ee9e0456854b4165975101152bc557a7a0a7b16e
@@@ -6,32 -6,89 +6,90 @@@ The format is based on [Keep a Changelo
  
  ## Unreleased
  
+ ### Removed
+ - MastoFE
+ ### Changed
+ - Allow users to remove their emails if instance does not need email to register
+ ### Added
+ - `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object
+ ### Fixed
+ - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
+ - Handle Reject for already-accepted Follows properly
++- Display OpenGraph data on alternative notice routes.
+ ### Removed
+ ## 2.4.1 - 2021-08-29
  ### Changed
+ - Make `mix pleroma.database set_text_search_config` run concurrently and indefinitely
  
+ ### Added
+ - AdminAPI: Missing configuration description for StealEmojiPolicy
+ ### Fixed
+ - MastodonAPI: Stream out Create activities
+ - MRF ObjectAgePolicy: Fix pattern matching on "published"
+ - TwitterAPI: Make `change_password` and `change_email` require params on body instead of query
+ - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
+ - AdminAPI: Fix rendering reports containing a `nil` object
+ - Mastodon API: Activity Search fallbacks on status fetching after a DB Timeout/Error
+ - Mastodon API: Fix crash in Streamer related to reblogging
+ - AdminAPI: List available frontends when `static/frontends` folder is missing
+ - Make activity search properly use language-aware GIN indexes
+ - AdminAPI: Fix suggestions for MRF Policies
+ ## 2.4.0 - 2021-08-08
+ ### Changed
+ - **Breaking:** Configuration: `:chat, enabled` moved to `:shout, enabled` and `:instance, chat_limit` moved to `:shout, limit`
+ - **Breaking** Entries for simple_policy, transparency_exclusions and quarantined_instances now list both the instance and a reason.
+ - Support for Erlang/OTP 24
  - The `application` metadata returned with statuses is no longer hardcoded. Apps that want to display these details will now have valid data for new posts after this change.
  - HTTPSecurityPlug now sends a response header to opt out of Google's FLoC (Federated Learning of Cohorts) targeted advertising.
+ - Email address is now returned if requesting user is the owner of the user account so it can be exposed in client and FE user settings UIs.
+ - Improved Twittercard and OpenGraph meta tag generation including thumbnails and image dimension metadata when available.
+ - AdminAPI: sort users so the newest are at the top.
+ - ActivityPub Client-to-Server(C2S): Limitation on the type of Activity/Object are lifted as they are now passed through ObjectValidators
  
  ### Added
  
  - MRF (`FollowBotPolicy`): New MRF Policy which makes a designated local Bot account attempt to follow all users in public Notes received by your instance. Users who require approving follower requests or have #nobot in their profile are excluded.
  - Return OAuth token `id` (primary key) in POST `/oauth/token`.
+ - AdminAPI: return `created_at` date with users.
+ - AdminAPI: add DELETE `/api/v1/pleroma/admin/instances/:instance` to delete all content from a remote instance.
+ - `AnalyzeMetadata` upload filter for extracting image/video attachment dimensions and generating blurhashes for images. Blurhashes for videos are not generated at this time.
+ - Attachment dimensions and blurhashes are federated when available.
+ - Mastodon API: support `poll` notification.
+ - Pinned posts federation
+ - AdminAPI: allow moderators to manage reports, users, invites, and custom emojis
  
  ### Fixed
  - Don't crash so hard when email settings are invalid.
- - Display OpenGraph data on alternative notice routes.
- ## Unreleased (Patch)
- ### Fixed
+ - Checking activated Upload Filters for required commands.
+ - Remote users can no longer reappear after being deleted.
+ - Deactivated users may now be deleted.
+ - Deleting an activity with a lot of likes/boosts no longer causes a database timeout.
+ - Mix task `pleroma.database prune_objects`
+ - Fixed rendering of JSON errors on ActivityPub endpoints.
+ - Linkify: Parsing crash with URLs ending in unbalanced closed paren, no path separator, and no query parameters
  - Try to save exported ConfigDB settings (migrate_from_db) in the system temp directory if default location is not writable.
  - Uploading custom instance thumbnail via AdminAPI/AdminFE generated invalid URL to the image
  - Applying ConcurrentLimiter settings via AdminAPI
  - User login failures if their `notification_settings` were in a NULL state.
  - Mix task `pleroma.user delete_activities` query transaction timeout is now :infinity
+ - MRF (`SimplePolicy`): Embedded objects are now checked. If any embedded object would be rejected, its parent is rejected. This fixes Announces leaking posts from blocked domains.
  - Fixed some Markdown issues, including trailing slash in links.
  
- ## [2.3.0] - 2020-03-01
+ ### Removed
+ - **Breaking**: Remove deprecated `/api/qvitter/statuses/notifications/read` (replaced by `/api/v1/pleroma/notifications/read`)
+ ## [2.3.0] - 2021-03-01
  
  ### Security
  
  - Support pagination of blocks and mutes.
  - Account backup.
  - Configuration: Add `:instance, autofollowing_nicknames` setting to provide a way to make accounts automatically follow new users that register on the local Pleroma instance.
+ - `[:activitypub, :blockers_visible]` config to control visibility of blockers.
  - Ability to view remote timelines, with ex. `/api/v1/timelines/public?instance=lain.com` and streams `public:remote` and `public:remote:media`.
  - The site title is now injected as a `title` tag like preloads or metadata.
  - Password reset tokens now are not accepted after a certain age.
    - Mastodon API: Support for expires_in/expires_at in the Filters.
  </details>
  
- ## [2.2.2] - 2020-01-18
+ ## [2.2.2] - 2021-01-18
  
  ### Fixed
  
index d41c4f7fe01a782fad5d80974561484e4c0695b9,9ce35ad6bd597d68ead6fd7af1bc72b4b139bfbc..e278036a269d3b279fc2f2fb7d7c39357362427b
@@@ -4,6 -4,7 +4,7 @@@
  
  defmodule Pleroma.Web.Router do
    use Pleroma.Web, :router
+   import Phoenix.LiveDashboard.Router
  
    pipeline :accepts_html do
      plug(:accepts, ["html"])
      plug(Pleroma.Web.Plugs.AdminSecretAuthenticationPlug)
      plug(:after_auth)
      plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug)
-     plug(Pleroma.Web.Plugs.UserIsAdminPlug)
+     plug(Pleroma.Web.Plugs.UserIsStaffPlug)
      plug(Pleroma.Web.Plugs.IdempotencyPlug)
    end
  
-   pipeline :mastodon_html do
-     plug(:browser)
-     plug(:authenticate)
-     plug(:after_auth)
+   pipeline :require_admin do
+     plug(Pleroma.Web.Plugs.UserIsAdminPlug)
    end
  
    pipeline :pleroma_html do
      plug(Pleroma.Web.Plugs.MappedSignatureToIdentityPlug)
    end
  
+   pipeline :static_fe do
+     plug(Pleroma.Web.Plugs.StaticFEPlug)
+   end
    scope "/api/v1/pleroma", Pleroma.Web.TwitterAPI do
      pipe_through(:pleroma_api)
  
      get("/emoji", UtilController, :emoji)
      get("/captcha", UtilController, :captcha)
      get("/healthcheck", UtilController, :healthcheck)
+     post("/remote_interaction", UtilController, :remote_interaction)
    end
  
    scope "/api/v1/pleroma", Pleroma.Web do
      post("/uploader_callback/:upload_path", UploaderController, :callback)
    end
  
+   # AdminAPI: only admins can perform these actions
    scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
-     pipe_through(:admin_api)
+     pipe_through([:admin_api, :require_admin])
  
      put("/users/disable_mfa", AdminAPIController, :disable_mfa)
-     put("/users/tag", AdminAPIController, :tag_users)
-     delete("/users/tag", AdminAPIController, :untag_users)
  
      get("/users/:nickname/permission_group", AdminAPIController, :right_get)
      get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
  
      post("/users/follow", UserController, :follow)
      post("/users/unfollow", UserController, :unfollow)
-     delete("/users", UserController, :delete)
      post("/users", UserController, :create)
+     patch("/users/suggest", UserController, :suggest)
+     patch("/users/unsuggest", UserController, :unsuggest)
+     get("/relay", RelayController, :index)
+     post("/relay", RelayController, :follow)
+     delete("/relay", RelayController, :unfollow)
+     get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
+     patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
+     get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
+     patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
+     get("/instance_document/:name", InstanceDocumentController, :show)
+     patch("/instance_document/:name", InstanceDocumentController, :update)
+     delete("/instance_document/:name", InstanceDocumentController, :delete)
+     patch("/users/confirm_email", AdminAPIController, :confirm_email)
+     patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
+     get("/config", ConfigController, :show)
+     post("/config", ConfigController, :update)
+     get("/config/descriptions", ConfigController, :descriptions)
+     get("/need_reboot", AdminAPIController, :need_reboot)
+     get("/restart", AdminAPIController, :restart)
+     get("/oauth_app", OAuthAppController, :index)
+     post("/oauth_app", OAuthAppController, :create)
+     patch("/oauth_app/:id", OAuthAppController, :update)
+     delete("/oauth_app/:id", OAuthAppController, :delete)
+     get("/media_proxy_caches", MediaProxyCacheController, :index)
+     post("/media_proxy_caches/delete", MediaProxyCacheController, :delete)
+     post("/media_proxy_caches/purge", MediaProxyCacheController, :purge)
+     get("/frontends", FrontendController, :index)
+     post("/frontends/install", FrontendController, :install)
+     post("/backups", AdminAPIController, :create_backup)
+   end
+   # AdminAPI: admins and mods (staff) can perform these actions
+   scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+     pipe_through(:admin_api)
+     put("/users/tag", AdminAPIController, :tag_users)
+     delete("/users/tag", AdminAPIController, :untag_users)
      patch("/users/:nickname/toggle_activation", UserController, :toggle_activation)
      patch("/users/activate", UserController, :activate)
      patch("/users/deactivate", UserController, :deactivate)
      patch("/users/approve", UserController, :approve)
  
-     get("/relay", RelayController, :index)
-     post("/relay", RelayController, :follow)
-     delete("/relay", RelayController, :unfollow)
+     delete("/users", UserController, :delete)
  
      post("/users/invite_token", InviteController, :create)
      get("/users/invites", InviteController, :index)
      get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
      get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
  
-     get("/instances/:instance/statuses", AdminAPIController, :list_instance_statuses)
-     get("/instance_document/:name", InstanceDocumentController, :show)
-     patch("/instance_document/:name", InstanceDocumentController, :update)
-     delete("/instance_document/:name", InstanceDocumentController, :delete)
-     patch("/users/confirm_email", AdminAPIController, :confirm_email)
-     patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
+     get("/instances/:instance/statuses", InstanceController, :list_statuses)
+     delete("/instances/:instance", InstanceController, :delete)
  
      get("/reports", ReportController, :index)
      get("/reports/:id", ReportController, :show)
      delete("/statuses/:id", StatusController, :delete)
      get("/statuses", StatusController, :index)
  
-     get("/config", ConfigController, :show)
-     post("/config", ConfigController, :update)
-     get("/config/descriptions", ConfigController, :descriptions)
-     get("/need_reboot", AdminAPIController, :need_reboot)
-     get("/restart", AdminAPIController, :restart)
      get("/moderation_log", AdminAPIController, :list_log)
  
      post("/reload_emoji", AdminAPIController, :reload_emoji)
      get("/stats", AdminAPIController, :stats)
  
-     get("/oauth_app", OAuthAppController, :index)
-     post("/oauth_app", OAuthAppController, :create)
-     patch("/oauth_app/:id", OAuthAppController, :update)
-     delete("/oauth_app/:id", OAuthAppController, :delete)
-     get("/media_proxy_caches", MediaProxyCacheController, :index)
-     post("/media_proxy_caches/delete", MediaProxyCacheController, :delete)
-     post("/media_proxy_caches/purge", MediaProxyCacheController, :purge)
      get("/chats/:id", ChatController, :show)
      get("/chats/:id/messages", ChatController, :messages)
      delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
-     get("/frontends", FrontendController, :index)
-     post("/frontends/install", FrontendController, :install)
-     post("/backups", AdminAPIController, :create_backup)
    end
  
    scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
      post("/accounts/:id/unblock", AccountController, :unblock)
      post("/accounts/:id/mute", AccountController, :mute)
      post("/accounts/:id/unmute", AccountController, :unmute)
+     post("/accounts/:id/note", AccountController, :note)
  
      get("/conversations", ConversationController, :index)
      post("/conversations/:id/read", ConversationController, :mark_as_read)
      delete("/push/subscription", SubscriptionController, :delete)
  
      get("/suggestions", SuggestionController, :index)
+     delete("/suggestions/:account_id", SuggestionController, :dismiss)
  
      get("/timelines/home", TimelineController, :home)
      get("/timelines/direct", TimelineController, :direct)
      get("/timelines/list/:list_id", TimelineController, :list)
    end
  
-   scope "/api/web", Pleroma.Web do
-     pipe_through(:authenticated_api)
-     # Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere
-     put("/settings", MastoFEController, :put_settings)
-   end
    scope "/api/v1", Pleroma.Web.MastodonAPI do
      pipe_through(:app_api)
  
      get("/search", SearchController, :search2)
  
      post("/media", MediaController, :create2)
+     get("/suggestions", SuggestionController, :index2)
    end
  
    scope "/api", Pleroma.Web do
  
      get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens)
      delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token)
-     post(
-       "/qvitter/statuses/notifications/read",
-       TwitterAPI.Controller,
-       :mark_notifications_as_read
-     )
    end
  
    scope "/", Pleroma.Web do
      # Note: html format is supported only if static FE is enabled
      # Note: http signature is only considered for json requests (no auth for non-json requests)
-     pipe_through([:accepts_html_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
+     pipe_through([:accepts_html_json, :http_signature, :static_fe])
  
      get("/objects/:uuid", OStatus.OStatusController, :object)
      get("/activities/:uuid", OStatus.OStatusController, :activity)
      get("/notice/:id", OStatus.OStatusController, :notice)
  
 +    # Notice compatibility routes for other frontends
 +    get("/@:nickname/:id", OStatus.OStatusController, :notice)
 +    get("/@:nickname/posts/:id", OStatus.OStatusController, :notice)
 +    get("/:nickname/status/:id", OStatus.OStatusController, :notice)
 +
      # Mastodon compatibility routes
      get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
      get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity)
    scope "/", Pleroma.Web do
      # Note: html format is supported only if static FE is enabled
      # Note: http signature is only considered for json requests (no auth for non-json requests)
-     pipe_through([:accepts_html_xml_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
+     pipe_through([:accepts_html_xml_json, :http_signature, :static_fe])
  
      # Note: returns user _profile_ for json requests, redirects to user _feed_ for non-json ones
      get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
  
    scope "/", Pleroma.Web do
      # Note: html format is supported only if static FE is enabled
-     pipe_through([:accepts_html_xml, Pleroma.Web.Plugs.StaticFEPlug])
+     pipe_through([:accepts_html_xml, :static_fe])
  
      get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
    end
    scope "/", Pleroma.Web do
      pipe_through(:api)
  
-     get("/web/manifest.json", MastoFEController, :manifest)
+     get("/manifest.json", ManifestController, :show)
    end
  
    scope "/", Pleroma.Web do
-     pipe_through(:mastodon_html)
-     get("/web/login", MastodonAPI.AuthController, :login)
-     delete("/auth/sign_out", MastodonAPI.AuthController, :logout)
-     post("/auth/password", MastodonAPI.AuthController, :password_reset)
-     get("/web/*path", MastoFEController, :index)
+     pipe_through(:pleroma_html)
  
-     get("/embed/:id", EmbedController, :show)
+     post("/auth/password", TwitterAPI.PasswordController, :request)
    end
  
-   scope "/proxy/", Pleroma.Web.MediaProxy do
-     get("/preview/:sig/:url", MediaProxyController, :preview)
-     get("/preview/:sig/:url/:filename", MediaProxyController, :preview)
-     get("/:sig/:url", MediaProxyController, :remote)
-     get("/:sig/:url/:filename", MediaProxyController, :remote)
+   scope "/proxy/", Pleroma.Web do
+     get("/preview/:sig/:url", MediaProxy.MediaProxyController, :preview)
+     get("/preview/:sig/:url/:filename", MediaProxy.MediaProxyController, :preview)
+     get("/:sig/:url", MediaProxy.MediaProxyController, :remote)
+     get("/:sig/:url/:filename", MediaProxy.MediaProxyController, :remote)
    end
  
    if Pleroma.Config.get(:env) == :dev do
      end
    end
  
+   scope "/" do
+     pipe_through([:pleroma_html, :authenticate, :require_admin])
+     live_dashboard("/phoenix/live_dashboard")
+   end
    # Test-only routes needed to test action dispatching and plug chain execution
    if Pleroma.Config.get(:env) == :test do
      @test_actions [
  
      options("/*path", RedirectController, :empty)
    end
+   # TODO: Change to Phoenix.Router.routes/1 for Phoenix 1.6.0+
+   def get_api_routes do
+     __MODULE__.__routes__()
+     |> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end)
+     |> Enum.map(fn r ->
+       r.path
+       |> String.split("/", trim: true)
+       |> List.first()
+     end)
+     |> Enum.uniq()
+   end
  end
index 42107063618045f837158abd5735cd3c664ae86b,50f0927a36c207863cb4a372856a8b1fa9245b51..827c0a384d4ab3b2ba0920ab54968e5684b367c1
@@@ -14,7 -14,6 +14,6 @@@ defmodule Pleroma.Web.StaticFE.StaticFE
    alias Pleroma.Web.Router.Helpers
  
    plug(:put_layout, :static_fe)
-   plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)
    plug(:assign_id)
  
    @page_keys ["max_id", "min_id", "limit", "since_id", "order"]
    defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
      do: assign(conn, :notice_id, notice_id)
  
 +  defp assign_id(%{path_info: ["@" <> _nickname, notice_id]} = conn, _opts),
 +    do: assign(conn, :notice_id, notice_id)
 +
 +  defp assign_id(%{path_info: ["@" <> _nickname, "posts", notice_id]} = conn, _opts),
 +    do: assign(conn, :notice_id, notice_id)
 +
 +  defp assign_id(%{path_info: [_nickname, "status", notice_id]} = conn, _opts),
 +    do: assign(conn, :notice_id, notice_id)
 +
    defp assign_id(%{path_info: ["users", user_id]} = conn, _opts),
      do: assign(conn, :username_or_id, user_id)
  
index fab042439ac9ce81f10e38c14501874d974f431d,81d66983763b1f879775d0ab1aaeafaaddc5f68f..b243e16921f1c856ba64eb31e7c7e5cf49fa7b67
@@@ -182,7 -182,7 +182,7 @@@ defmodule Pleroma.Web.OStatus.OStatusCo
          |> response(200)
  
        assert resp =~
-                "<meta content=\"#{Pleroma.Web.base_url()}/notice/#{note_activity.id}\" property=\"og:url\">"
+                "<meta content=\"#{Pleroma.Web.Endpoint.url()}/notice/#{note_activity.id}\" property=\"og:url\">"
  
        user = insert(:user)
  
        |> response(200)
      end
    end
 +
 +  describe "notice compatibility routes" do
 +    test "Soapbox FE", %{conn: conn} do
 +      user = insert(:user)
 +      note_activity = insert(:note_activity, user: user)
 +
 +      resp =
 +        conn
 +        |> put_req_header("accept", "text/html")
 +        |> get("/@#{user.nickname}/posts/#{note_activity.id}")
 +        |> response(200)
 +
 +      expected =
 +        "<meta content=\"#{Pleroma.Web.base_url()}/notice/#{note_activity.id}\" property=\"og:url\">"
 +
 +      assert resp =~ expected
 +    end
 +
 +    test "Mastodon", %{conn: conn} do
 +      user = insert(:user)
 +      note_activity = insert(:note_activity, user: user)
 +
 +      resp =
 +        conn
 +        |> put_req_header("accept", "text/html")
 +        |> get("/@#{user.nickname}/#{note_activity.id}")
 +        |> response(200)
 +
 +      expected =
 +        "<meta content=\"#{Pleroma.Web.base_url()}/notice/#{note_activity.id}\" property=\"og:url\">"
 +
 +      assert resp =~ expected
 +    end
 +
 +    test "Twitter", %{conn: conn} do
 +      user = insert(:user)
 +      note_activity = insert(:note_activity, user: user)
 +
 +      resp =
 +        conn
 +        |> put_req_header("accept", "text/html")
 +        |> get("/#{user.nickname}/status/#{note_activity.id}")
 +        |> response(200)
 +
 +      expected =
 +        "<meta content=\"#{Pleroma.Web.base_url()}/notice/#{note_activity.id}\" property=\"og:url\">"
 +
 +      assert resp =~ expected
 +    end
 +  end
  end
index 7596a9a5467efacd1d11efce204afe4c369f30b8,52379b86a6d3355cb0076afb47228afc198bde74..4b3925ad2fd57f056e87bad47386338d03ae8f12
@@@ -86,8 -86,6 +86,8 @@@ defmodule Pleroma.Web.Plugs.FrontendSta
        "objects",
        "activities",
        "notice",
 +      "@:nickname",
 +      ":nickname",
        "users",
        "tags",
        "mailer",
        "internal",
        ".well-known",
        "nodeinfo",
-       "web",
+       "manifest.json",
        "auth",
-       "embed",
        "proxy",
+       "phoenix",
        "test",
        "user_exists",
        "check_password"
      ]
  
-     assert expected_routes == Pleroma.Web.get_api_routes()
+     assert expected_routes == Pleroma.Web.Router.get_api_routes()
    end
  end