diff options
40 files changed, 1077 insertions, 304 deletions
diff --git a/config/config.exs b/config/config.exs index a620e7451..a867dd0bc 100644 --- a/config/config.exs +++ b/config/config.exs @@ -215,6 +215,9 @@ config :pleroma, :frontend_configurations,      scopeCopy: true,      subjectLineBehavior: "email",      alwaysShowSubjectInput: true +  }, +  masto_fe: %{ +    showInstanceSpecificPanel: true    }  config :pleroma, :activitypub, diff --git a/config/test.exs b/config/test.exs index fbeba0919..6dfa698c8 100644 --- a/config/test.exs +++ b/config/test.exs @@ -44,6 +44,8 @@ config :web_push_encryption, :vapid_details,      "BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",    private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA" +config :web_push_encryption, :http_client, Pleroma.Web.WebPushHttpClientMock +  config :pleroma, Pleroma.Jobs, testing: [max_jobs: 2]  try do diff --git a/docs/Admin-API.md b/docs/Admin-API.md index 407647645..2edb31f3c 100644 --- a/docs/Admin-API.md +++ b/docs/Admin-API.md @@ -7,36 +7,11 @@ Authentication is required and the user must be an admin.  ### List users  - Method `GET` -- Params: -  - `page`: **integer** page number -  - `page_size`: **integer** number of users per page (default is `50`) -- Response: - -```JSON -{ -  "page_size": integer, -  "count": integer, -  "users": [ -    { -      "deactivated": bool, -      "id": integer, -      "nickname": string -    }, -    ... -  ] -} -``` - -## `/api/pleroma/admin/users/search?query={query}&local={local}&page={page}&page_size={page_size}` - -### Search users by name or nickname - -- Method `GET` -- Params: -  - `query`: **string** search term -  - `local`: **bool** whether to return only local users -  - `page`: **integer** page number -  - `page_size`: **integer** number of users per page (default is `50`) +- Query Params: +  - `query`: **string** *optional* search term +  - `local_only`: **bool** *optional* whether to return only local users +  - `page`: **integer** *optional* page number +  - `page_size`: **integer** *optional* number of users per page (default is `50`)  - Response:  ```JSON diff --git a/docs/config.md b/docs/config.md index d1bf2a6f4..465bc1d2b 100644 --- a/docs/config.md +++ b/docs/config.md @@ -129,7 +129,7 @@ See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_s  ## :frontend_configurations -This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` are configured. +This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` and `masto_fe` are configured.  Frontends can access these settings at `/api/pleroma/frontend_configurations` diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex index 057553e24..f701aaaa5 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/plugs/http_security_plug.ex @@ -34,13 +34,16 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do    defp csp_string do      scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme] -    websocket_url = String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws") +    static_url = Pleroma.Web.Endpoint.static_url() +    websocket_url = String.replace(static_url, "http", "ws") + +    connect_src = "connect-src 'self' #{static_url} #{websocket_url}"      connect_src =        if Mix.env() == :dev do -        "connect-src 'self' http://localhost:3035/ " <> websocket_url +        connect_src <> " http://localhost:3035/"        else -        "connect-src 'self' " <> websocket_url +        connect_src        end      script_src = diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 91a5db8c5..1a97e9fde 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -85,6 +85,10 @@ defmodule Pleroma.Upload do      end    end +  def char_unescaped?(char) do +    URI.char_unreserved?(char) or char == ?/ +  end +    defp get_opts(opts) do      {size_limit, activity_type} =        case Keyword.get(opts, :type) do @@ -218,9 +222,7 @@ defmodule Pleroma.Upload do    defp url_from_spec(base_url, {:file, path}) do      path =        path -      |> URI.encode() -      |> String.replace("?", "%3F") -      |> String.replace(":", "%3A") +      |> URI.encode(&char_unescaped?/1)      [base_url, "media", path]      |> Path.join() diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 50e7e7ccd..3878e4efa 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -749,13 +749,41 @@ defmodule Pleroma.User do      Repo.all(query)    end -  @spec search_for_admin(binary(), %{ +  @spec search_for_admin(%{ +          local: boolean(), +          page: number(), +          page_size: number() +        }) :: {:ok, [Pleroma.User.t()], number()} +  def search_for_admin(%{query: nil, local: local, page: page, page_size: page_size}) do +    query = +      from(u in User, order_by: u.id) +      |> maybe_local_user_query(local) + +    paginated_query = +      query +      |> paginate(page, page_size) + +    count = +      query +      |> Repo.aggregate(:count, :id) + +    {:ok, Repo.all(paginated_query), count} +  end + +  @spec search_for_admin(%{ +          query: binary(),            admin: Pleroma.User.t(),            local: boolean(),            page: number(),            page_size: number()          }) :: {:ok, [Pleroma.User.t()], number()} -  def search_for_admin(term, %{admin: admin, local: local, page: page, page_size: page_size}) do +  def search_for_admin(%{ +        query: term, +        admin: admin, +        local: local, +        page: page, +        page_size: page_size +      }) do      term = String.trim_leading(term, "@")      local_paginated_query = @@ -774,21 +802,6 @@ defmodule Pleroma.User do      {:ok, do_search(search_query, admin), count}    end -  @spec all_for_admin(number(), number()) :: {:ok, [Pleroma.User.t()], number()} -  def all_for_admin(page, page_size) do -    query = from(u in User, order_by: u.id) - -    paginated_query = -      query -      |> paginate(page, page_size) - -    count = -      query -      |> Repo.aggregate(:count, :id) - -    {:ok, Repo.all(paginated_query), count} -  end -    def search(query, resolve \\ false, for_user \\ nil) do      # Strip the beginning @ off if there is a query      query = String.trim_leading(query, "@") diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 88f4779c8..9e50789db 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do    alias Pleroma.Web    alias Pleroma.Object    alias Pleroma.Activity +  alias Pleroma.Web.ActivityPub.Visibility    alias Pleroma.User    alias Pleroma.Notification    alias Pleroma.Web.Router.Helpers @@ -274,13 +275,31 @@ defmodule Pleroma.Web.ActivityPub.Utils do      Repo.all(query)    end -  def make_like_data(%User{ap_id: ap_id} = actor, %{data: %{"id" => id}} = object, activity_id) do +  def make_like_data( +        %User{ap_id: ap_id} = actor, +        %{data: %{"actor" => object_actor_id, "id" => id}} = object, +        activity_id +      ) do +    object_actor = User.get_cached_by_ap_id(object_actor_id) + +    to = +      if Visibility.is_public?(object) do +        [actor.follower_address, object.data["actor"]] +      else +        [object.data["actor"]] +      end + +    cc = +      (object.data["to"] ++ (object.data["cc"] || [])) +      |> List.delete(actor.ap_id) +      |> List.delete(object_actor.follower_address) +      data = %{        "type" => "Like",        "actor" => ap_id,        "object" => id, -      "to" => [actor.follower_address, object.data["actor"]], -      "cc" => ["https://www.w3.org/ns/activitystreams#Public"], +      "to" => to, +      "cc" => cc,        "context" => object.data["context"]      } diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index aae02cab8..75c2c6061 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -63,28 +63,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do           do: json_response(conn, :no_content, "")    end -  def list_users(conn, params) do -    {page, page_size} = page_params(params) - -    with {:ok, users, count} <- User.all_for_admin(page, page_size), -         do: -           conn -           |> json( -             AccountView.render("index.json", -               users: users, -               count: count, -               page_size: page_size -             ) -           ) -  end - -  def search_users(%{assigns: %{user: admin}} = conn, %{"query" => query} = params) do +  def list_users(%{assigns: %{user: admin}} = conn, params) do      {page, page_size} = page_params(params)      with {:ok, users, count} <- -           User.search_for_admin(query, %{ +           User.search_for_admin(%{ +             query: params["query"],               admin: admin, -             local: params["local"] == "true", +             local: params["local_only"] == "true",               page: page,               page_size: page_size             }), diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 7114d6de6..55a9c2572 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -14,6 +14,19 @@ defmodule Pleroma.Web.CommonAPI do    import Pleroma.Web.CommonAPI.Utils +  def follow(follower, followed) do +    with {:ok, follower} <- User.maybe_direct_follow(follower, followed), +         {:ok, activity} <- ActivityPub.follow(follower, followed), +         {:ok, follower, followed} <- +           User.wait_and_refresh( +             Pleroma.Config.get([:activitypub, :follow_handshake_timeout]), +             follower, +             followed +           ) do +      {:ok, follower, followed, activity} +    end +  end +    def delete(activity_id, user) do      with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id),           %Object{} = object <- Object.normalize(object_id), diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 056be49b0..8c58f4545 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -15,14 +15,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    alias Pleroma.Web    alias Pleroma.Web.CommonAPI    alias Pleroma.Web.MediaProxy -  alias Pleroma.Web.Push -  alias Push.Subscription    alias Pleroma.Web.MastodonAPI.AccountView    alias Pleroma.Web.MastodonAPI.FilterView    alias Pleroma.Web.MastodonAPI.ListView    alias Pleroma.Web.MastodonAPI.MastodonView -  alias Pleroma.Web.MastodonAPI.PushSubscriptionView    alias Pleroma.Web.MastodonAPI.StatusView    alias Pleroma.Web.MastodonAPI.ReportView    alias Pleroma.Web.ActivityPub.ActivityPub @@ -193,6 +190,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    end    defp add_link_headers(conn, method, activities, param \\ nil, params \\ %{}) do +    params = +      conn.params +      |> Map.drop(["since_id", "max_id"]) +      |> Map.merge(params) +      last = List.last(activities)      first = List.first(activities) @@ -292,13 +294,17 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    end    def dm_timeline(%{assigns: %{user: user}} = conn, params) do -    query = -      ActivityPub.fetch_activities_query( -        [user.ap_id], -        Map.merge(params, %{"type" => "Create", visibility: "direct"}) -      ) +    params = +      params +      |> Map.put("type", "Create") +      |> Map.put("blocking_user", user) +      |> Map.put("user", user) +      |> Map.put(:visibility, "direct") -    activities = Repo.all(query) +    activities = +      [user.ap_id] +      |> ActivityPub.fetch_activities_query(params) +      |> Repo.all()      conn      |> add_link_headers(:dm_timeline, activities) @@ -733,14 +739,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do      with %User{} = followed <- Repo.get(User, id), -         {:ok, follower} <- User.maybe_direct_follow(follower, followed), -         {:ok, _activity} <- ActivityPub.follow(follower, followed), -         {:ok, follower, followed} <- -           User.wait_and_refresh( -             Config.get([:activitypub, :follow_handshake_timeout]), -             follower, -             followed -           ) do +         {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do        conn        |> put_view(AccountView)        |> render("relationship.json", %{user: follower, target: followed}) @@ -754,8 +753,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do      with %User{} = followed <- Repo.get_by(User, nickname: uri), -         {:ok, follower} <- User.maybe_direct_follow(follower, followed), -         {:ok, _activity} <- ActivityPub.follow(follower, followed) do +         {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do        conn        |> put_view(AccountView)        |> render("account.json", %{user: followed, for: follower}) @@ -1424,37 +1422,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      json(conn, %{})    end -  def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do -    true = Push.enabled() -    Subscription.delete_if_exists(user, token) -    {:ok, subscription} = Subscription.create(user, token, params) -    view = PushSubscriptionView.render("push_subscription.json", subscription: subscription) -    json(conn, view) -  end - -  def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do -    true = Push.enabled() -    subscription = Subscription.get(user, token) -    view = PushSubscriptionView.render("push_subscription.json", subscription: subscription) -    json(conn, view) -  end - -  def update_push_subscription( -        %{assigns: %{user: user, token: token}} = conn, -        params -      ) do -    true = Push.enabled() -    {:ok, subscription} = Subscription.update(user, token, params) -    view = PushSubscriptionView.render("push_subscription.json", subscription: subscription) -    json(conn, view) -  end - -  def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do -    true = Push.enabled() -    {:ok, _response} = Subscription.delete(user, token) -    json(conn, %{}) -  end - +  # fallback action +  #    def errors(conn, _) do      conn      |> put_status(500) diff --git a/lib/pleroma/web/mastodon_api/subscription_controller.ex b/lib/pleroma/web/mastodon_api/subscription_controller.ex new file mode 100644 index 000000000..b6c8ff808 --- /dev/null +++ b/lib/pleroma/web/mastodon_api/subscription_controller.ex @@ -0,0 +1,71 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.SubscriptionController do +  @moduledoc "The module represents functions to manage user subscriptions." +  use Pleroma.Web, :controller + +  alias Pleroma.Web.Push +  alias Pleroma.Web.Push.Subscription +  alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View + +  action_fallback(:errors) + +  # Creates PushSubscription +  # POST /api/v1/push/subscription +  # +  def create(%{assigns: %{user: user, token: token}} = conn, params) do +    with true <- Push.enabled(), +         {:ok, _} <- Subscription.delete_if_exists(user, token), +         {:ok, subscription} <- Subscription.create(user, token, params) do +      view = View.render("push_subscription.json", subscription: subscription) +      json(conn, view) +    end +  end + +  # Gets PushSubscription +  # GET /api/v1/push/subscription +  # +  def get(%{assigns: %{user: user, token: token}} = conn, _params) do +    with true <- Push.enabled(), +         {:ok, subscription} <- Subscription.get(user, token) do +      view = View.render("push_subscription.json", subscription: subscription) +      json(conn, view) +    end +  end + +  # Updates PushSubscription +  # PUT /api/v1/push/subscription +  # +  def update(%{assigns: %{user: user, token: token}} = conn, params) do +    with true <- Push.enabled(), +         {:ok, subscription} <- Subscription.update(user, token, params) do +      view = View.render("push_subscription.json", subscription: subscription) +      json(conn, view) +    end +  end + +  # Deletes PushSubscription +  # DELETE /api/v1/push/subscription +  # +  def delete(%{assigns: %{user: user, token: token}} = conn, _params) do +    with true <- Push.enabled(), +         {:ok, _response} <- Subscription.delete(user, token), +         do: json(conn, %{}) +  end + +  # fallback action +  # +  def errors(conn, {:error, :not_found}) do +    conn +    |> put_status(404) +    |> json("Not found") +  end + +  def errors(conn, _) do +    conn +    |> put_status(500) +    |> json("Something went wrong") +  end +end diff --git a/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex b/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex index e86b789c5..021489711 100644 --- a/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex +++ b/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex @@ -4,6 +4,7 @@  defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do    use Pleroma.Web, :view +  alias Pleroma.Web.Push    def render("push_subscription.json", %{subscription: subscription}) do      %{ @@ -14,7 +15,5 @@ defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do      }    end -  defp server_key do -    Keyword.get(Application.get_env(:web_push_encryption, :vapid_details), :public_key) -  end +  defp server_key, do: Keyword.get(Push.vapid_config(), :public_key)  end diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex index a166800d4..5fc9c9e7b 100644 --- a/lib/pleroma/web/metadata/utils.ex +++ b/lib/pleroma/web/metadata/utils.ex @@ -17,14 +17,14 @@ defmodule Pleroma.Web.Metadata.Utils do      |> Formatter.truncate()    end -  def scrub_html_and_truncate(content) when is_binary(content) do +  def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) do      content      # html content comes from DB already encoded, decode first and scrub after      |> HtmlEntities.decode()      |> String.replace(~r/<br\s?\/?>/, " ")      |> HTML.strip_tags()      |> Formatter.demojify() -    |> Formatter.truncate() +    |> Formatter.truncate(max_length)    end    def attachment_url(url) do diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex index f4867d05b..8c775ce24 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex @@ -6,7 +6,6 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do    use Pleroma.Web, :controller    alias Pleroma.Config -  alias Pleroma.Repo    alias Pleroma.Stats    alias Pleroma.User    alias Pleroma.Web @@ -86,8 +85,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do        end      staff_accounts = -      User.moderator_user_query() -      |> Repo.all() +      User.all_superusers()        |> Enum.map(fn u -> u.ap_id end)      mrf_user_allowlist = diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex new file mode 100644 index 000000000..33f912d34 --- /dev/null +++ b/lib/pleroma/web/push/impl.ex @@ -0,0 +1,127 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Push.Impl do +  @moduledoc "The module represents implementation push web notification" + +  alias Pleroma.Repo +  alias Pleroma.User +  alias Pleroma.Activity +  alias Pleroma.Object +  alias Pleroma.Web.Push.Subscription +  alias Pleroma.Web.Metadata.Utils +  alias Pleroma.Notification + +  require Logger +  import Ecto.Query + +  @types ["Create", "Follow", "Announce", "Like"] + +  @doc "Performs sending notifications for user subscriptions" +  @spec perform_send(Notification.t()) :: list(any) +  def perform_send(%{activity: %{data: %{"type" => activity_type}}, user_id: user_id} = notif) +      when activity_type in @types do +    actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) + +    type = Activity.mastodon_notification_type(notif.activity) +    gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key) +    avatar_url = User.avatar_url(actor) + +    for subscription <- fetch_subsriptions(user_id), +        get_in(subscription.data, ["alerts", type]) do +      %{ +        title: format_title(notif), +        access_token: subscription.token.token, +        body: format_body(notif, actor), +        notification_id: notif.id, +        notification_type: type, +        icon: avatar_url, +        preferred_locale: "en" +      } +      |> Jason.encode!() +      |> push_message(build_sub(subscription), gcm_api_key, subscription) +    end +  end + +  def perform_send(_) do +    Logger.warn("Unknown notification type") +    :error +  end + +  @doc "Push message to web" +  def push_message(body, sub, api_key, subscription) do +    case WebPushEncryption.send_web_push(body, sub, api_key) do +      {:ok, %{status_code: code}} when 400 <= code and code < 500 -> +        Logger.debug("Removing subscription record") +        Repo.delete!(subscription) +        :ok + +      {:ok, %{status_code: code}} when 200 <= code and code < 300 -> +        :ok + +      {:ok, %{status_code: code}} -> +        Logger.error("Web Push Notification failed with code: #{code}") +        :error + +      _ -> +        Logger.error("Web Push Notification failed with unknown error") +        :error +    end +  end + +  @doc "Gets user subscriptions" +  def fetch_subsriptions(user_id) do +    Subscription +    |> where(user_id: ^user_id) +    |> preload(:token) +    |> Repo.all() +  end + +  def build_sub(subscription) do +    %{ +      keys: %{ +        p256dh: subscription.key_p256dh, +        auth: subscription.key_auth +      }, +      endpoint: subscription.endpoint +    } +  end + +  def format_body( +        %{activity: %{data: %{"type" => "Create", "object" => %{"content" => content}}}}, +        actor +      ) do +    "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" +  end + +  def format_body( +        %{activity: %{data: %{"type" => "Announce", "object" => activity_id}}}, +        actor +      ) do +    %Activity{data: %{"object" => %{"id" => object_id}}} = Activity.get_by_ap_id(activity_id) +    %Object{data: %{"content" => content}} = Object.get_by_ap_id(object_id) + +    "@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}" +  end + +  def format_body( +        %{activity: %{data: %{"type" => type}}}, +        actor +      ) +      when type in ["Follow", "Like"] do +    case type do +      "Follow" -> "@#{actor.nickname} has followed you" +      "Like" -> "@#{actor.nickname} has favorited your post" +    end +  end + +  def format_title(%{activity: %{data: %{"type" => type}}}) do +    case type do +      "Create" -> "New Mention" +      "Follow" -> "New Follower" +      "Announce" -> "New Repeat" +      "Like" -> "New Favorite" +    end +  end +end diff --git a/lib/pleroma/web/push/push.ex b/lib/pleroma/web/push/push.ex index ddd4fe037..951dab535 100644 --- a/lib/pleroma/web/push/push.ex +++ b/lib/pleroma/web/push/push.ex @@ -5,14 +5,13 @@  defmodule Pleroma.Web.Push do    use GenServer -  alias Pleroma.Repo -  alias Pleroma.User -  alias Pleroma.Web.Push.Subscription +  alias Pleroma.Web.Push.Impl    require Logger -  import Ecto.Query -  @types ["Create", "Follow", "Announce", "Like"] +  ############## +  # Client API # +  ##############    def start_link() do      GenServer.start_link(__MODULE__, :ok, name: __MODULE__) @@ -30,14 +29,18 @@ defmodule Pleroma.Web.Push do      end    end -  def send(notification) do -    if enabled() do -      GenServer.cast(Pleroma.Web.Push, {:send, notification}) -    end -  end +  def send(notification), +    do: GenServer.cast(__MODULE__, {:send, notification}) + +  #################### +  # Server Callbacks # +  #################### +  @impl true    def init(:ok) do -    if !enabled() do +    if enabled() do +      {:ok, nil} +    else        Logger.warn("""        VAPID key pair is not found. If you wish to enabled web push, please run @@ -47,93 +50,15 @@ defmodule Pleroma.Web.Push do        """)        :ignore -    else -      {:ok, nil}      end    end -  def handle_cast( -        {:send, %{activity: %{data: %{"type" => type}}, user_id: user_id} = notification}, -        state -      ) -      when type in @types do -    actor = User.get_cached_by_ap_id(notification.activity.data["actor"]) - -    type = Pleroma.Activity.mastodon_notification_type(notification.activity) - -    Subscription -    |> where(user_id: ^user_id) -    |> preload(:token) -    |> Repo.all() -    |> Enum.filter(fn subscription -> -      get_in(subscription.data, ["alerts", type]) || false -    end) -    |> Enum.each(fn subscription -> -      sub = %{ -        keys: %{ -          p256dh: subscription.key_p256dh, -          auth: subscription.key_auth -        }, -        endpoint: subscription.endpoint -      } - -      body = -        Jason.encode!(%{ -          title: format_title(notification), -          access_token: subscription.token.token, -          body: format_body(notification, actor), -          notification_id: notification.id, -          notification_type: type, -          icon: User.avatar_url(actor), -          preferred_locale: "en" -        }) - -      case WebPushEncryption.send_web_push( -             body, -             sub, -             Application.get_env(:web_push_encryption, :gcm_api_key) -           ) do -        {:ok, %{status_code: code}} when 400 <= code and code < 500 -> -          Logger.debug("Removing subscription record") -          Repo.delete!(subscription) -          :ok - -        {:ok, %{status_code: code}} when 200 <= code and code < 300 -> -          :ok - -        {:ok, %{status_code: code}} -> -          Logger.error("Web Push Notification failed with code: #{code}") -          :error - -        _ -> -          Logger.error("Web Push Notification failed with unknown error") -          :error -      end -    end) - -    {:noreply, state} -  end - -  def handle_cast({:send, _}, state) do -    Logger.warn("Unknown notification type") -    {:noreply, state} -  end - -  defp format_title(%{activity: %{data: %{"type" => type}}}) do -    case type do -      "Create" -> "New Mention" -      "Follow" -> "New Follower" -      "Announce" -> "New Repeat" -      "Like" -> "New Favorite" +  @impl true +  def handle_cast({:send, notification}, state) do +    if enabled() do +      Impl.perform_send(notification)      end -  end -  defp format_body(%{activity: %{data: %{"type" => type}}}, actor) do -    case type do -      "Create" -> "@#{actor.nickname} has mentioned you" -      "Follow" -> "@#{actor.nickname} has followed you" -      "Announce" -> "@#{actor.nickname} has repeated your post" -      "Like" -> "@#{actor.nickname} has favorited your post" -    end +    {:noreply, state}    end  end diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex index 242e30910..c90bd2bda 100644 --- a/lib/pleroma/web/push/subscription.ex +++ b/lib/pleroma/web/push/subscription.ex @@ -12,6 +12,8 @@ defmodule Pleroma.Web.Push.Subscription do    alias Pleroma.Web.OAuth.Token    alias Pleroma.Web.Push.Subscription +  @type t :: %__MODULE__{} +    schema "push_subscriptions" do      belongs_to(:user, User, type: Pleroma.FlakeId)      belongs_to(:token, Token) @@ -50,24 +52,32 @@ defmodule Pleroma.Web.Push.Subscription do      })    end +  @doc "Gets subsciption by user & token" +  @spec get(User.t(), Token.t()) :: {:ok, t()} | {:error, :not_found}    def get(%User{id: user_id}, %Token{id: token_id}) do -    Repo.get_by(Subscription, user_id: user_id, token_id: token_id) +    case Repo.get_by(Subscription, user_id: user_id, token_id: token_id) do +      nil -> {:error, :not_found} +      subscription -> {:ok, subscription} +    end    end    def update(user, token, params) do -    get(user, token) -    |> change(data: alerts(params)) -    |> Repo.update() +    with {:ok, subscription} <- get(user, token) do +      subscription +      |> change(data: alerts(params)) +      |> Repo.update() +    end    end    def delete(user, token) do -    Repo.delete(get(user, token)) +    with {:ok, subscription} <- get(user, token), +         do: Repo.delete(subscription)    end    def delete_if_exists(user, token) do      case get(user, token) do -      nil -> {:ok, nil} -      sub -> Repo.delete(sub) +      {:error, _} -> {:ok, nil} +      {:ok, sub} -> Repo.delete(sub)      end    end diff --git a/lib/pleroma/web/rel_me.ex b/lib/pleroma/web/rel_me.ex index a07db966f..ab29a36e3 100644 --- a/lib/pleroma/web/rel_me.ex +++ b/lib/pleroma/web/rel_me.ex @@ -28,7 +28,8 @@ defmodule Pleroma.Web.RelMe do      {:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options)      data = -      Floki.attribute(html, "link[rel=me]", "href") ++ Floki.attribute(html, "a[rel=me]", "href") +      Floki.attribute(html, "link[rel~=me]", "href") ++ +        Floki.attribute(html, "a[rel~=me]", "href")      {:ok, data}    rescue diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index abb1cf7f2..8317a1162 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -8,10 +8,24 @@ defmodule Pleroma.Web.RichMedia.Helpers do    alias Pleroma.HTML    alias Pleroma.Web.RichMedia.Parser +  defp validate_page_url(page_url) when is_binary(page_url) do +    if AutoLinker.Parser.is_url?(page_url, true) do +      URI.parse(page_url) |> validate_page_url +    else +      :error +    end +  end + +  defp validate_page_url(%URI{authority: nil}), do: :error +  defp validate_page_url(%URI{scheme: nil}), do: :error +  defp validate_page_url(%URI{}), do: :ok +  defp validate_page_url(_), do: :error +    def fetch_data_for_activity(%Activity{} = activity) do      with true <- Pleroma.Config.get([:rich_media, :enabled]),           %Object{} = object <- Object.normalize(activity.data["object"]),           {:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]), +         :ok <- validate_page_url(page_url),           {:ok, rich_media} <- Parser.parse(page_url) do        %{page_url: page_url, rich_media: rich_media}      else diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 6fcb46878..65a90e31e 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -140,7 +140,6 @@ defmodule Pleroma.Web.Router do      pipe_through([:admin_api, :oauth_write])      get("/users", AdminAPIController, :list_users) -    get("/users/search", AdminAPIController, :search_users)      delete("/user", AdminAPIController, :user_delete)      patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)      post("/user", AdminAPIController, :user_create) @@ -304,10 +303,10 @@ defmodule Pleroma.Web.Router do      scope [] do        pipe_through(:oauth_push) -      post("/push/subscription", MastodonAPIController, :create_push_subscription) -      get("/push/subscription", MastodonAPIController, :get_push_subscription) -      put("/push/subscription", MastodonAPIController, :update_push_subscription) -      delete("/push/subscription", MastodonAPIController, :delete_push_subscription) +      post("/push/subscription", SubscriptionController, :create) +      get("/push/subscription", SubscriptionController, :get) +      put("/push/subscription", SubscriptionController, :update) +      delete("/push/subscription", SubscriptionController, :delete)      end    end diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index ab6470d78..dcb15b9a9 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -28,18 +28,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do    end    def follow(%User{} = follower, params) do -    with {:ok, %User{} = followed} <- get_user(params), -         {:ok, follower} <- User.maybe_direct_follow(follower, followed), -         {:ok, activity} <- ActivityPub.follow(follower, followed), -         {:ok, follower, followed} <- -           User.wait_and_refresh( -             Pleroma.Config.get([:activitypub, :follow_handshake_timeout]), -             follower, -             followed -           ) do -      {:ok, follower, followed, activity} -    else -      err -> err +    with {:ok, %User{} = followed} <- get_user(params) do +      CommonAPI.follow(follower, followed)      end    end diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index de7b9f24c..5e4ebb8e8 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -177,13 +177,16 @@ defmodule Pleroma.Web.TwitterAPI.Controller do    end    def dm_timeline(%{assigns: %{user: user}} = conn, params) do -    query = -      ActivityPub.fetch_activities_query( -        [user.ap_id], -        Map.merge(params, %{"type" => "Create", "user" => user, visibility: "direct"}) -      ) +    params = +      params +      |> Map.put("type", "Create") +      |> Map.put("blocking_user", user) +      |> Map.put("user", user) +      |> Map.put(:visibility, "direct") -    activities = Repo.all(query) +    activities = +      ActivityPub.fetch_activities_query([user.ap_id], params) +      |> Repo.all()      conn      |> put_view(ActivityView) @@ -76,7 +76,7 @@ defmodule Pleroma.Mixfile do        {:ex_aws, "~> 2.0"},        {:ex_aws_s3, "~> 2.0"},        {:earmark, "~> 1.3"}, -      {:ex_machina, "~> 2.2", only: :test}, +      {:ex_machina, "~> 2.3", only: :test},        {:credo, "~> 0.9.3", only: [:dev, :test]},        {:mock, "~> 0.3.1", only: :test},        {:crypt, @@ -14,14 +14,14 @@    "credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},    "crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},    "db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, -  "decimal": {:hex, :decimal, "1.6.0", "bfd84d90ff966e1f5d4370bdd3943432d8f65f07d3bab48001aebd7030590dcc", [:mix], [], "hexpm"}, +  "decimal": {:hex, :decimal, "1.7.0", "30d6b52c88541f9a66637359ddf85016df9eb266170d53105f02e4a67e00c5aa", [:mix], [], "hexpm"},    "earmark": {:hex, :earmark, "1.3.0", "17f0c38eaafb4800f746b457313af4b2442a8c2405b49c645768680f900be603", [:mix], [], "hexpm"}, -  "ecto": {:hex, :ecto, "2.2.10", "e7366dc82f48f8dd78fcbf3ab50985ceeb11cb3dc93435147c6e13f2cda0992e", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, +  "ecto": {:hex, :ecto, "2.2.11", "4bb8f11718b72ba97a2696f65d247a379e739a0ecabf6a13ad1face79844791c", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},    "eternal": {:hex, :eternal, "1.2.0", "e2a6b6ce3b8c248f7dc31451aefca57e3bdf0e48d73ae5043229380a67614c41", [:mix], [], "hexpm"},    "ex_aws": {:hex, :ex_aws, "2.1.0", "b92651527d6c09c479f9013caa9c7331f19cba38a650590d82ebf2c6c16a1d8a", [:mix], [{:configparser_ex, "~> 2.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "1.6.3 or 1.6.5 or 1.7.1 or 1.8.6 or ~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8", [hex: :jsx, repo: "hexpm", optional: true]}, {:poison, ">= 1.2.0", [hex: :poison, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:xml_builder, "~> 0.1.0", [hex: :xml_builder, repo: "hexpm", optional: true]}], "hexpm"},    "ex_aws_s3": {:hex, :ex_aws_s3, "2.0.1", "9e09366e77f25d3d88c5393824e613344631be8db0d1839faca49686e99b6704", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm"},    "ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, -  "ex_machina": {:hex, :ex_machina, "2.2.0", "fec496331e04fc2db2a1a24fe317c12c0c4a50d2beb8ebb3531ed1f0d84be0ed", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, +  "ex_machina": {:hex, :ex_machina, "2.3.0", "92a5ad0a8b10ea6314b876a99c8c9e3f25f4dde71a2a835845b136b9adaf199a", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm"},    "ex_syslogger": {:git, "https://github.com/slashmili/ex_syslogger.git", "f3963399047af17e038897c69e20d552e6899e1d", [tag: "1.4.0"]},    "floki": {:hex, :floki, "0.20.4", "be42ac911fece24b4c72f3b5846774b6e61b83fe685c2fc9d62093277fb3bc86", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}, {:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},    "gen_smtp": {:hex, :gen_smtp, "0.13.0", "11f08504c4bdd831dc520b8f84a1dce5ce624474a797394e7aafd3c29f5dcd25", [:rebar3], [], "hexpm"}, @@ -53,7 +53,7 @@    "plug_cowboy": {:hex, :plug_cowboy, "2.0.1", "d798f8ee5acc86b7d42dbe4450b8b0dadf665ce588236eb0a751a132417a980e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},    "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},    "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, -  "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"}, +  "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},    "postgrex": {:hex, :postgrex, "0.13.5", "3d931aba29363e1443da167a4b12f06dcd171103c424de15e5f3fc2ba3e6d9c5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"},    "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},    "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, diff --git a/test/fixtures/rel_me_anchor_nofollow.html b/test/fixtures/rel_me_anchor_nofollow.html new file mode 100644 index 000000000..c856f0091 --- /dev/null +++ b/test/fixtures/rel_me_anchor_nofollow.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +	<head> +		<meta charset="utf-8"/> +		<title>Blog</title> +	</head> +	<body> +	<article> +		<h1>Lorem ipsum</h1> +		<p>Lorem ipsum dolor sit ameph, …</p> +		<a rel="me nofollow" href="https://social.example.org/users/lain">lain’s account</a> +	</article> +	</body> +</html> diff --git a/test/fixtures/rel_me_null.html b/test/fixtures/rel_me_null.html index 57d424b80..5ab5f10c1 100644 --- a/test/fixtures/rel_me_null.html +++ b/test/fixtures/rel_me_null.html @@ -8,6 +8,7 @@  	<article>  		<h1>Lorem ipsum</h1>  		<p>Lorem ipsum dolor sit ameph, …</p> +		<a rel="nofollow" href="https://social.example.org/users/lain">lain’s account</a>  	</article>  	</body>  </html> diff --git a/test/support/factory.ex b/test/support/factory.ex index d1956d1cd..18f77f01a 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -23,7 +23,7 @@ defmodule Pleroma.Factory do      }    end -  def note_factory do +  def note_factory(attrs \\ %{}) do      text = sequence(:text, &"This is :moominmamma: note #{&1}")      user = insert(:user) @@ -46,7 +46,7 @@ defmodule Pleroma.Factory do      }      %Pleroma.Object{ -      data: data +      data: merge_attributes(data, Map.get(attrs, :data, %{}))      }    end @@ -95,8 +95,8 @@ defmodule Pleroma.Factory do      }    end -  def note_activity_factory do -    note = insert(:note) +  def note_activity_factory(attrs \\ %{}) do +    note = attrs[:note] || insert(:note)      data = %{        "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), @@ -135,9 +135,9 @@ defmodule Pleroma.Factory do      }    end -  def announce_activity_factory do -    note_activity = insert(:note_activity) -    user = insert(:user) +  def announce_activity_factory(attrs \\ %{}) do +    note_activity = attrs[:note_activity] || insert(:note_activity) +    user = attrs[:user] || insert(:user)      data = %{        "type" => "Announce", @@ -229,15 +229,32 @@ defmodule Pleroma.Factory do    end    def oauth_token_factory do -    user = insert(:user)      oauth_app = insert(:oauth_app)      %Pleroma.Web.OAuth.Token{        token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(),        refresh_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(), -      user_id: user.id, +      user: build(:user),        app_id: oauth_app.id,        valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10)      }    end + +  def push_subscription_factory do +    %Pleroma.Web.Push.Subscription{ +      user: build(:user), +      token: build(:oauth_token), +      endpoint: "https://example.com/example/1234", +      key_auth: "8eDyX_uCN0XRhSbY5hs7Hg==", +      key_p256dh: +        "BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA=", +      data: %{} +    } +  end + +  def notification_factory do +    %Pleroma.Notification{ +      user: build(:user) +    } +  end  end diff --git a/test/support/web_push_http_client_mock.ex b/test/support/web_push_http_client_mock.ex new file mode 100644 index 000000000..d8accd21c --- /dev/null +++ b/test/support/web_push_http_client_mock.ex @@ -0,0 +1,23 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.WebPushHttpClientMock do +  def get(url, headers \\ [], options \\ []) do +    { +      res, +      %Tesla.Env{status: status} +    } = Pleroma.HTTP.request(:get, url, "", headers, options) + +    {res, %{status_code: status}} +  end + +  def post(url, body, headers \\ [], options \\ []) do +    { +      res, +      %Tesla.Env{status: status} +    } = Pleroma.HTTP.request(:post, url, body, headers, options) + +    {res, %{status_code: status}} +  end +end diff --git a/test/upload_test.exs b/test/upload_test.exs index b2d9eca38..bdda01b3f 100644 --- a/test/upload_test.exs +++ b/test/upload_test.exs @@ -153,19 +153,20 @@ defmodule Pleroma.UploadTest do        assert Path.basename(attachment_url["href"]) == "an%E2%80%A6%20image.jpg"      end -    test "replaces : (colon) and ? (question-mark) to %3A and %3F (respectively)" do +    test "escapes reserved uri characters" do        File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")        file = %Plug.Upload{          content_type: "image/jpg",          path: Path.absname("test/fixtures/image_tmp.jpg"), -        filename: "is:an?image.jpg" +        filename: ":?#[]@!$&\\'()*+,;=.jpg"        }        {:ok, data} = Upload.store(file)        [attachment_url | _] = data["url"] -      assert Path.basename(attachment_url["href"]) == "is%3Aan%3Fimage.jpg" +      assert Path.basename(attachment_url["href"]) == +               "%3A%3F%23%5B%5D%40%21%24%26%5C%27%28%29%2A%2B%2C%3B%3D.jpg"      end    end  end diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs index aeed0564c..2e5e95795 100644 --- a/test/web/activity_pub/utils_test.exs +++ b/test/web/activity_pub/utils_test.exs @@ -1,7 +1,10 @@  defmodule Pleroma.Web.ActivityPub.UtilsTest do    use Pleroma.DataCase +  alias Pleroma.Web.CommonAPI    alias Pleroma.Web.ActivityPub.Utils +  import Pleroma.Factory +    describe "determine_explicit_mentions()" do      test "works with an object that has mentions" do        object = %{ @@ -54,4 +57,51 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do        assert Utils.determine_explicit_mentions(object) == []      end    end + +  describe "make_like_data" do +    setup do +      user = insert(:user) +      other_user = insert(:user) +      third_user = insert(:user) +      [user: user, other_user: other_user, third_user: third_user] +    end + +    test "addresses actor's follower address if the activity is public", %{ +      user: user, +      other_user: other_user, +      third_user: third_user +    } do +      expected_to = Enum.sort([user.ap_id, other_user.follower_address]) +      expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id]) + +      {:ok, activity} = +        CommonAPI.post(user, %{ +          "status" => +            "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?" +        }) + +      %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil) +      assert Enum.sort(to) == expected_to +      assert Enum.sort(cc) == expected_cc +    end + +    test "does not adress actor's follower address if the activity is not public", %{ +      user: user, +      other_user: other_user, +      third_user: third_user +    } do +      expected_to = Enum.sort([user.ap_id]) +      expected_cc = [third_user.ap_id] + +      {:ok, activity} = +        CommonAPI.post(user, %{ +          "status" => "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!", +          "visibility" => "private" +        }) + +      %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil) +      assert Enum.sort(to) == expected_to +      assert Enum.sort(cc) == expected_cc +    end +  end  end diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 42e0daf8e..1b8b4d4b7 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -374,26 +374,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                 "users" => []               }      end -  end - -  test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do -    admin = insert(:user, info: %{is_admin: true}) -    user = insert(:user) - -    conn = -      build_conn() -      |> assign(:user, admin) -      |> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation") -    assert json_response(conn, 200) == -             %{ -               "deactivated" => !user.info.deactivated, -               "id" => user.id, -               "nickname" => user.nickname -             } -  end - -  describe "GET /api/pleroma/admin/users/search" do      test "regular search" do        admin = insert(:user, info: %{is_admin: true})        user = insert(:user, nickname: "bob") @@ -401,7 +382,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        conn =          build_conn()          |> assign(:user, admin) -        |> get("/api/pleroma/admin/users/search?query=bo") +        |> get("/api/pleroma/admin/users?query=bo")        assert json_response(conn, 200) == %{                 "count" => 1, @@ -424,7 +405,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        conn =          build_conn()          |> assign(:user, admin) -        |> get("/api/pleroma/admin/users/search?query=bo&page_size=1&page=1") +        |> get("/api/pleroma/admin/users?query=bo&page_size=1&page=1")        assert json_response(conn, 200) == %{                 "count" => 2, @@ -441,7 +422,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        conn =          build_conn()          |> assign(:user, admin) -        |> get("/api/pleroma/admin/users/search?query=bo&page_size=1&page=2") +        |> get("/api/pleroma/admin/users?query=bo&page_size=1&page=2")        assert json_response(conn, 200) == %{                 "count" => 2, @@ -465,7 +446,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        conn =          build_conn()          |> assign(:user, admin) -        |> get("/api/pleroma/admin/users/search?query=bo&local=true") +        |> get("/api/pleroma/admin/users?query=bo&local_only=true")        assert json_response(conn, 200) == %{                 "count" => 1, @@ -479,5 +460,51 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do                 ]               }      end + +    test "only local users with no query" do +      admin = insert(:user, info: %{is_admin: true}, nickname: "john") +      user = insert(:user, nickname: "bob") + +      insert(:user, nickname: "bobb", local: false) + +      conn = +        build_conn() +        |> assign(:user, admin) +        |> get("/api/pleroma/admin/users?local_only=true") + +      assert json_response(conn, 200) == %{ +               "count" => 2, +               "page_size" => 50, +               "users" => [ +                 %{ +                   "deactivated" => admin.info.deactivated, +                   "id" => admin.id, +                   "nickname" => admin.nickname +                 }, +                 %{ +                   "deactivated" => user.info.deactivated, +                   "id" => user.id, +                   "nickname" => user.nickname +                 } +               ] +             } +    end +  end + +  test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do +    admin = insert(:user, info: %{is_admin: true}) +    user = insert(:user) + +    conn = +      build_conn() +      |> assign(:user, admin) +      |> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation") + +    assert json_response(conn, 200) == +             %{ +               "deactivated" => !user.info.deactivated, +               "id" => user.id, +               "nickname" => user.nickname +             }    end  end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index f7f10662a..ffba5e3de 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -248,6 +248,33 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      assert status["url"] != direct.data["id"]    end +  test "doesn't include DMs from blocked users", %{conn: conn} do +    blocker = insert(:user) +    blocked = insert(:user) +    user = insert(:user) +    {:ok, blocker} = User.block(blocker, blocked) + +    {:ok, _blocked_direct} = +      CommonAPI.post(blocked, %{ +        "status" => "Hi @#{blocker.nickname}!", +        "visibility" => "direct" +      }) + +    {:ok, direct} = +      CommonAPI.post(user, %{ +        "status" => "Hi @#{blocker.nickname}!", +        "visibility" => "direct" +      }) + +    res_conn = +      conn +      |> assign(:user, user) +      |> get("api/v1/timelines/direct") + +    [status] = json_response(res_conn, 200) +    assert status["id"] == direct.id +  end +    test "replying to a status", %{conn: conn} do      user = insert(:user) @@ -1928,4 +1955,36 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do                 |> json_response(400)      end    end + +  describe "link headers" do +    test "preserves parameters in link headers", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) + +      {:ok, activity1} = +        CommonAPI.post(other_user, %{ +          "status" => "hi @#{user.nickname}", +          "visibility" => "public" +        }) + +      {:ok, activity2} = +        CommonAPI.post(other_user, %{ +          "status" => "hi @#{user.nickname}", +          "visibility" => "public" +        }) + +      notification1 = Repo.get_by(Notification, activity_id: activity1.id) +      notification2 = Repo.get_by(Notification, activity_id: activity2.id) + +      conn = +        conn +        |> assign(:user, user) +        |> get("/api/v1/notifications", %{media_only: true}) + +      assert [link_header] = get_resp_header(conn, "link") +      assert link_header =~ ~r/media_only=true/ +      assert link_header =~ ~r/since_id=#{notification2.id}/ +      assert link_header =~ ~r/max_id=#{notification1.id}/ +    end +  end  end diff --git a/test/web/mastodon_api/push_subscription_view_test.exs b/test/web/mastodon_api/push_subscription_view_test.exs new file mode 100644 index 000000000..dc935fc82 --- /dev/null +++ b/test/web/mastodon_api/push_subscription_view_test.exs @@ -0,0 +1,23 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.PushSubscriptionViewTest do +  use Pleroma.DataCase +  import Pleroma.Factory +  alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View +  alias Pleroma.Web.Push + +  test "Represent a subscription" do +    subscription = insert(:push_subscription, data: %{"alerts" => %{"mention" => true}}) + +    expected = %{ +      alerts: %{"mention" => true}, +      endpoint: subscription.endpoint, +      id: to_string(subscription.id), +      server_key: Keyword.get(Push.vapid_config(), :public_key) +    } + +    assert expected == View.render("push_subscription.json", %{subscription: subscription}) +  end +end diff --git a/test/web/mastodon_api/subscription_controller_test.exs b/test/web/mastodon_api/subscription_controller_test.exs new file mode 100644 index 000000000..7dfb02f63 --- /dev/null +++ b/test/web/mastodon_api/subscription_controller_test.exs @@ -0,0 +1,192 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do +  use Pleroma.Web.ConnCase + +  import Pleroma.Factory +  alias Pleroma.Web.Push +  alias Pleroma.Web.Push.Subscription + +  @sub %{ +    "endpoint" => "https://example.com/example/1234", +    "keys" => %{ +      "auth" => "8eDyX_uCN0XRhSbY5hs7Hg==", +      "p256dh" => +        "BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA=" +    } +  } +  @server_key Keyword.get(Push.vapid_config(), :public_key) + +  setup do +    user = insert(:user) +    token = insert(:oauth_token, user: user, scopes: ["push"]) + +    conn = +      build_conn() +      |> assign(:user, user) +      |> assign(:token, token) + +    %{conn: conn, user: user, token: token} +  end + +  defmacro assert_error_when_disable_push(do: yield) do +    quote do +      vapid_details = Application.get_env(:web_push_encryption, :vapid_details, []) +      Application.put_env(:web_push_encryption, :vapid_details, []) +      assert "Something went wrong" == unquote(yield) +      Application.put_env(:web_push_encryption, :vapid_details, vapid_details) +    end +  end + +  describe "creates push subscription" do +    test "returns error when push disabled ", %{conn: conn} do +      assert_error_when_disable_push do +        conn +        |> post("/api/v1/push/subscription", %{}) +        |> json_response(500) +      end +    end + +    test "successful creation", %{conn: conn} do +      result = +        conn +        |> post("/api/v1/push/subscription", %{ +          "data" => %{"alerts" => %{"mention" => true, "test" => true}}, +          "subscription" => @sub +        }) +        |> json_response(200) + +      [subscription] = Pleroma.Repo.all(Subscription) + +      assert %{ +               "alerts" => %{"mention" => true}, +               "endpoint" => subscription.endpoint, +               "id" => to_string(subscription.id), +               "server_key" => @server_key +             } == result +    end +  end + +  describe "gets a user subscription" do +    test "returns error when push disabled ", %{conn: conn} do +      assert_error_when_disable_push do +        conn +        |> get("/api/v1/push/subscription", %{}) +        |> json_response(500) +      end +    end + +    test "returns error when user hasn't subscription", %{conn: conn} do +      res = +        conn +        |> get("/api/v1/push/subscription", %{}) +        |> json_response(404) + +      assert "Not found" == res +    end + +    test "returns a user subsciption", %{conn: conn, user: user, token: token} do +      subscription = +        insert(:push_subscription, +          user: user, +          token: token, +          data: %{"alerts" => %{"mention" => true}} +        ) + +      res = +        conn +        |> get("/api/v1/push/subscription", %{}) +        |> json_response(200) + +      expect = %{ +        "alerts" => %{"mention" => true}, +        "endpoint" => "https://example.com/example/1234", +        "id" => to_string(subscription.id), +        "server_key" => @server_key +      } + +      assert expect == res +    end +  end + +  describe "updates a user subsciption" do +    setup %{conn: conn, user: user, token: token} do +      subscription = +        insert(:push_subscription, +          user: user, +          token: token, +          data: %{"alerts" => %{"mention" => true}} +        ) + +      %{conn: conn, user: user, token: token, subscription: subscription} +    end + +    test "returns error when push disabled ", %{conn: conn} do +      assert_error_when_disable_push do +        conn +        |> put("/api/v1/push/subscription", %{data: %{"alerts" => %{"mention" => false}}}) +        |> json_response(500) +      end +    end + +    test "returns updated subsciption", %{conn: conn, subscription: subscription} do +      res = +        conn +        |> put("/api/v1/push/subscription", %{ +          data: %{"alerts" => %{"mention" => false, "follow" => true}} +        }) +        |> json_response(200) + +      expect = %{ +        "alerts" => %{"follow" => true, "mention" => false}, +        "endpoint" => "https://example.com/example/1234", +        "id" => to_string(subscription.id), +        "server_key" => @server_key +      } + +      assert expect == res +    end +  end + +  describe "deletes the user subscription" do +    test "returns error when push disabled ", %{conn: conn} do +      assert_error_when_disable_push do +        conn +        |> delete("/api/v1/push/subscription", %{}) +        |> json_response(500) +      end +    end + +    test "returns error when user hasn't subscription", %{conn: conn} do +      res = +        conn +        |> delete("/api/v1/push/subscription", %{}) +        |> json_response(404) + +      assert "Not found" == res +    end + +    test "returns empty result and delete user subsciption", %{ +      conn: conn, +      user: user, +      token: token +    } do +      subscription = +        insert(:push_subscription, +          user: user, +          token: token, +          data: %{"alerts" => %{"mention" => true}} +        ) + +      res = +        conn +        |> delete("/api/v1/push/subscription", %{}) +        |> json_response(200) + +      assert %{} == res +      refute Pleroma.Repo.get(Subscription, subscription.id) +    end +  end +end diff --git a/test/web/node_info_test.exs b/test/web/node_info_test.exs index 763549bd1..038feecc1 100644 --- a/test/web/node_info_test.exs +++ b/test/web/node_info_test.exs @@ -8,7 +8,8 @@ defmodule Pleroma.Web.NodeInfoTest do    import Pleroma.Factory    test "nodeinfo shows staff accounts", %{conn: conn} do -    user = insert(:user, %{local: true, info: %{is_moderator: true}}) +    moderator = insert(:user, %{local: true, info: %{is_moderator: true}}) +    admin = insert(:user, %{local: true, info: %{is_admin: true}})      conn =        conn @@ -16,7 +17,8 @@ defmodule Pleroma.Web.NodeInfoTest do      assert result = json_response(conn, 200) -    assert user.ap_id in result["metadata"]["staffAccounts"] +    assert moderator.ap_id in result["metadata"]["staffAccounts"] +    assert admin.ap_id in result["metadata"]["staffAccounts"]    end    test "nodeinfo shows restricted nicknames", %{conn: conn} do diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs new file mode 100644 index 000000000..3f9f3d809 --- /dev/null +++ b/test/web/push/impl_test.exs @@ -0,0 +1,145 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Push.ImplTest do +  use Pleroma.DataCase + +  alias Pleroma.Web.Push.Impl +  alias Pleroma.Web.Push.Subscription + +  import Pleroma.Factory + +  setup_all do +    Tesla.Mock.mock_global(fn +      %{method: :post, url: "https://example.com/example/1234"} -> +        %Tesla.Env{status: 200} + +      %{method: :post, url: "https://example.com/example/not_found"} -> +        %Tesla.Env{status: 400} + +      %{method: :post, url: "https://example.com/example/bad"} -> +        %Tesla.Env{status: 100} +    end) + +    :ok +  end + +  @sub %{ +    endpoint: "https://example.com/example/1234", +    keys: %{ +      auth: "8eDyX_uCN0XRhSbY5hs7Hg==", +      p256dh: +        "BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA=" +    } +  } +  @api_key "BASgACIHpN1GYgzSRp" +  @message "@Bob: Lorem ipsum dolor sit amet, consectetur  adipiscing elit. Fusce sagittis fini..." + +  test "performs sending notifications" do +    user = insert(:user) +    user2 = insert(:user) +    insert(:push_subscription, user: user, data: %{alerts: %{"mention" => true}}) +    insert(:push_subscription, user: user2, data: %{alerts: %{"mention" => true}}) + +    insert(:push_subscription, +      user: user, +      data: %{alerts: %{"follow" => true, "mention" => true}} +    ) + +    insert(:push_subscription, +      user: user, +      data: %{alerts: %{"follow" => true, "mention" => false}} +    ) + +    notif = +      insert(:notification, +        user: user, +        activity: %Pleroma.Activity{ +          data: %{ +            "type" => "Create", +            "actor" => user.ap_id, +            "object" => %{"content" => "<Lorem ipsum dolor sit amet."} +          } +        } +      ) + +    assert Impl.perform_send(notif) == [:ok, :ok] +  end + +  test "returns error if notif does not match " do +    assert Impl.perform_send(%{}) == :error +  end + +  test "successful message sending" do +    assert Impl.push_message(@message, @sub, @api_key, %Subscription{}) == :ok +  end + +  test "fail message sending" do +    assert Impl.push_message( +             @message, +             Map.merge(@sub, %{endpoint: "https://example.com/example/bad"}), +             @api_key, +             %Subscription{} +           ) == :error +  end + +  test "delete subsciption if restult send message between 400..500" do +    subscription = insert(:push_subscription) + +    assert Impl.push_message( +             @message, +             Map.merge(@sub, %{endpoint: "https://example.com/example/not_found"}), +             @api_key, +             subscription +           ) == :ok + +    refute Pleroma.Repo.get(Subscription, subscription.id) +  end + +  test "renders body for create activity" do +    assert Impl.format_body( +             %{ +               activity: %{ +                 data: %{ +                   "type" => "Create", +                   "object" => %{ +                     "content" => +                       "<span>Lorem ipsum dolor sit amet</span>, consectetur :bear: adipiscing elit. Fusce sagittis finibus turpis." +                   } +                 } +               } +             }, +             %{nickname: "Bob"} +           ) == +             "@Bob: Lorem ipsum dolor sit amet, consectetur  adipiscing elit. Fusce sagittis fini..." +  end + +  test "renders body for follow activity" do +    assert Impl.format_body(%{activity: %{data: %{"type" => "Follow"}}}, %{nickname: "Bob"}) == +             "@Bob has followed you" +  end + +  test "renders body for announce activity" do +    user = insert(:user) + +    note = +      insert(:note, %{ +        data: %{ +          "content" => +            "<span>Lorem ipsum dolor sit amet</span>, consectetur :bear: adipiscing elit. Fusce sagittis finibus turpis." +        } +      }) + +    note_activity = insert(:note_activity, %{note: note}) +    announce_activity = insert(:announce_activity, %{user: user, note_activity: note_activity}) + +    assert Impl.format_body(%{activity: announce_activity}, user) == +             "@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur  adipiscing elit. Fusce sagittis fini..." +  end + +  test "renders body for like activity" do +    assert Impl.format_body(%{activity: %{data: %{"type" => "Like"}}}, %{nickname: "Bob"}) == +             "@Bob has favorited your post" +  end +end diff --git a/test/web/rel_me_test.exs b/test/web/rel_me_test.exs index ba8038e69..5188f4de1 100644 --- a/test/web/rel_me_test.exs +++ b/test/web/rel_me_test.exs @@ -11,6 +11,12 @@ defmodule Pleroma.Web.RelMeTest do        %{          method: :get, +        url: "http://example.com/rel_me/anchor_nofollow" +      } -> +        %Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_anchor_nofollow.html")} + +      %{ +        method: :get,          url: "http://example.com/rel_me/link"        } ->          %Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_link.html")} @@ -33,6 +39,7 @@ defmodule Pleroma.Web.RelMeTest do      assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/link") == {:ok, hrefs}      assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor") == {:ok, hrefs} +    assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor_nofollow") == {:ok, hrefs}    end    test "maybe_put_rel_me/2" do @@ -49,6 +56,11 @@ defmodule Pleroma.Web.RelMeTest do      assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/anchor", profile_urls) ==               attr +    assert Pleroma.Web.RelMe.maybe_put_rel_me( +             "http://example.com/rel_me/anchor_nofollow", +             profile_urls +           ) == attr +      assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/link", profile_urls) ==               attr    end diff --git a/test/web/rich_media/helpers_test.exs b/test/web/rich_media/helpers_test.exs new file mode 100644 index 000000000..60d93768f --- /dev/null +++ b/test/web/rich_media/helpers_test.exs @@ -0,0 +1,62 @@ +defmodule Pleroma.Web.RichMedia.HelpersTest do +  use Pleroma.DataCase + +  alias Pleroma.Web.CommonAPI + +  import Pleroma.Factory +  import Tesla.Mock + +  setup do +    mock(fn env -> apply(HttpRequestMock, :request, [env]) end) +    :ok +  end + +  test "refuses to crawl incomplete URLs" do +    user = insert(:user) + +    {:ok, activity} = +      CommonAPI.post(user, %{ +        "status" => "[test](example.com/ogp)", +        "content_type" => "text/markdown" +      }) + +    Pleroma.Config.put([:rich_media, :enabled], true) + +    assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + +    Pleroma.Config.put([:rich_media, :enabled], false) +  end + +  test "refuses to crawl malformed URLs" do +    user = insert(:user) + +    {:ok, activity} = +      CommonAPI.post(user, %{ +        "status" => "[test](example.com[]/ogp)", +        "content_type" => "text/markdown" +      }) + +    Pleroma.Config.put([:rich_media, :enabled], true) + +    assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + +    Pleroma.Config.put([:rich_media, :enabled], false) +  end + +  test "crawls valid, complete URLs" do +    user = insert(:user) + +    {:ok, activity} = +      CommonAPI.post(user, %{ +        "status" => "[test](http://example.com/ogp)", +        "content_type" => "text/markdown" +      }) + +    Pleroma.Config.put([:rich_media, :enabled], true) + +    assert %{page_url: "http://example.com/ogp", rich_media: _} = +             Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + +    Pleroma.Config.put([:rich_media, :enabled], false) +  end +end diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index d18b65876..ce0812308 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -415,6 +415,33 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do        assert status["id"] == direct_two.id        assert status_two["id"] == direct.id      end + +    test "doesn't include DMs from blocked users", %{conn: conn} do +      blocker = insert(:user) +      blocked = insert(:user) +      user = insert(:user) +      {:ok, blocker} = User.block(blocker, blocked) + +      {:ok, _blocked_direct} = +        CommonAPI.post(blocked, %{ +          "status" => "Hi @#{blocker.nickname}!", +          "visibility" => "direct" +        }) + +      {:ok, direct} = +        CommonAPI.post(user, %{ +          "status" => "Hi @#{blocker.nickname}!", +          "visibility" => "direct" +        }) + +      res_conn = +        conn +        |> assign(:user, blocker) +        |> get("/api/statuses/dm_timeline.json") + +      [status] = json_response(res_conn, 200) +      assert status["id"] == direct.id +    end    end    describe "GET /statuses/mentions.json" do @@ -1762,8 +1789,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do          |> assign(:user, user)          |> post("/api/pleroma/friendships/approve", %{"user_id" => other_user.id}) -      user = Repo.get(User, user.id) -        assert relationship = json_response(conn, 200)        assert other_user.id == relationship["id"]        assert relationship["follows_you"] == true @@ -1787,8 +1812,6 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do          |> assign(:user, user)          |> post("/api/pleroma/friendships/deny", %{"user_id" => other_user.id}) -      user = Repo.get(User, user.id) -        assert relationship = json_response(conn, 200)        assert other_user.id == relationship["id"]        assert relationship["follows_you"] == false  | 
