diff --git a/.formatter.exs b/.formatter.exs index 2bed17cc0..7fa95a619 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,3 +1,3 @@ [ - inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] + inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}", "priv/repo/migrations/*.exs"] ] diff --git a/CHANGELOG.md b/CHANGELOG.md index b42b13018..bd06ec866 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Pleroma API: `GET /api/v1/pleroma/accounts/:id/scrobbles` to get a list of recently scrobbled items - Pleroma API: `POST /api/v1/pleroma/scrobble` to scrobble a media item - Mastodon API: Add `upload_limit`, `avatar_upload_limit`, `background_upload_limit`, and `banner_upload_limit` to `/api/v1/instance` +- Mastodon API: Add `pleroma.unread_conversation_count` to the Account entity +- OAuth: support for hierarchical permissions / [Mastodon 2.4.3 OAuth permissions](https://docs.joinmastodon.org/api/permissions/) +- Authentication: Added rate limit for password-authorized actions / login existence checks +- Metadata Link: Atom syndication Feed +- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`) ### Changed - **Breaking:** Elixir >=1.8 is now required (was >= 1.7) @@ -21,11 +26,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Admin API: Return `total` when querying for reports - Mastodon API: Return `pleroma.direct_conversation_id` when creating a direct message (`POST /api/v1/statuses`) - Admin API: Return link alongside with token on password reset +- MRF (Simple Policy): Also use `:accept`/`:reject` on the actors rather than only their activities +- OStatus: Extract RSS functionality +- Mastodon API: Add `pleroma.direct_conversation_id` to the status endpoint (`GET /api/v1/statuses/:id`) ### Fixed - Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`) - Mastodon API: Inability to get some local users by nickname in `/api/v1/accounts/:id_or_nickname` - Added `:instance, extended_nickname_format` setting to the default config +- Report emails now include functional links to profiles of remote user accounts ## [1.1.0] - 2019-??-?? ### Security @@ -78,6 +87,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - ActivityPub: Fix `/users/:nickname/inbox` crashing without an authenticated user - MRF: fix ability to follow a relay when AntiFollowbotPolicy was enabled - Mastodon API: Blocks are now treated consistently between the Streaming API and the Timeline APIs +- Mastodon API: `exclude_replies` is correctly handled again. ### Added - Expiring/ephemeral activites. All activities can have expires_at value set, which controls when they should be deleted automatically. diff --git a/config/config.exs b/config/config.exs index ddbfb246a..f4d92102f 100644 --- a/config/config.exs +++ b/config/config.exs @@ -409,7 +409,8 @@ providers: [ Pleroma.Web.Metadata.Providers.OpenGraph, Pleroma.Web.Metadata.Providers.TwitterCard, - Pleroma.Web.Metadata.Providers.RelMe + Pleroma.Web.Metadata.Providers.RelMe, + Pleroma.Web.Metadata.Providers.Feed ], unfurl_nsfw: false @@ -588,7 +589,7 @@ config :http_signatures, adapter: Pleroma.Signature -config :pleroma, :rate_limit, nil +config :pleroma, :rate_limit, authentication: {60_000, 15} config :pleroma, Pleroma.ActivityExpiration, enabled: true diff --git a/config/description.exs b/config/description.exs index 4547ea368..b007cf69c 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2290,7 +2290,8 @@ group: :pleroma, key: :rate_limit, type: :group, - description: "Rate limit settings. This is an advanced feature and disabled by default.", + description: + "Rate limit settings. This is an advanced feature enabled only for :authentication by default.", children: [ %{ key: :search, @@ -2329,6 +2330,12 @@ description: "for fav / unfav or reblog / unreblog actions on the same status by the same user", suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]] + }, + %{ + key: :authentication, + type: [:tuple, {:list, :tuple}], + description: "for authentication create / password check / user existence check requests", + suggestions: [{60_000, 15}] } ] }, diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md index d007a69c3..21b297529 100644 --- a/docs/API/differences_in_mastoapi_responses.md +++ b/docs/API/differences_in_mastoapi_responses.md @@ -56,6 +56,7 @@ Has these additional fields under the `pleroma` object: - `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials` - `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials` - `deactivated`: boolean, true when the user is deactivated +- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner. ### Source diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index b86799ecc..8d85276ed 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -475,6 +475,7 @@ config :pleroma, :workers, * Pleroma.Web.Metadata.Providers.OpenGraph * Pleroma.Web.Metadata.Providers.TwitterCard * Pleroma.Web.Metadata.Providers.RelMe - add links from user bio with rel=me into the `
` as `` + * Pleroma.Web.Metadata.Providers.Feed - add a link to a user's Atom feed into the `
` as `` * `unfurl_nsfw`: If set to `true` nsfw attachments will be shown in previews ## :rich_media diff --git a/lib/mix/tasks/pleroma/count_statuses.ex b/lib/mix/tasks/pleroma/count_statuses.ex new file mode 100644 index 000000000..e1e8195dd --- /dev/null +++ b/lib/mix/tasks/pleroma/count_statuses.ex @@ -0,0 +1,22 @@ +defmodule Mix.Tasks.Pleroma.CountStatuses do + @shortdoc "Re-counts statuses for all users" + + use Mix.Task + alias Pleroma.User + import Ecto.Query + + def run([]) do + Mix.Pleroma.start_pleroma() + + stream = + User + |> where(local: true) + |> Pleroma.Repo.stream() + + Pleroma.Repo.transaction(fn -> + Enum.each(stream, &User.update_note_count/1) + end) + + Mix.Pleroma.shell_info("Done") + end +end diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index be5821ad7..098016af2 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -67,6 +67,8 @@ def create_or_bump_for(activity, opts \\ []) do participations = Enum.map(users, fn user -> + User.increment_unread_conversation_count(conversation, user) + {:ok, participation} = Participation.create_for_user_and_conversation(user, conversation, opts) diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index e946f6de2..ab81f3217 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -52,6 +52,15 @@ def mark_as_read(participation) do participation |> read_cng(%{read: true}) |> Repo.update() + |> case do + {:ok, participation} -> + participation = Repo.preload(participation, :user) + User.set_unread_conversation_count(participation.user) + {:ok, participation} + + error -> + error + end end def mark_as_unread(participation) do @@ -135,4 +144,12 @@ def set_recipients(participation, user_ids) do {:ok, Repo.preload(participation, :recipients, force: true)} end + + def unread_conversation_count_for_user(user) do + from(p in __MODULE__, + where: p.user_id == ^user.id, + where: not p.read, + select: %{count: count(p.id)} + ) + end end diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex index c14be02dd..b15e4041b 100644 --- a/lib/pleroma/emails/admin_email.ex +++ b/lib/pleroma/emails/admin_email.ex @@ -17,7 +17,7 @@ defp instance_notify_email do end defp user_url(user) do - Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, user.nickname) + Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, user.id) end def report(to, reporter, account, statuses, comment) do diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex index b508628a9..a3278dbef 100644 --- a/lib/pleroma/plugs/oauth_scopes_plug.ex +++ b/lib/pleroma/plugs/oauth_scopes_plug.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do import Plug.Conn import Pleroma.Web.Gettext + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + @behaviour Plug def init(%{scopes: _} = options), do: options @@ -13,24 +15,26 @@ def init(%{scopes: _} = options), do: options def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do op = options[:op] || :| token = assigns[:token] + matched_scopes = token && filter_descendants(scopes, token.scopes) cond do is_nil(token) -> + maybe_perform_instance_privacy_check(conn, options) + + op == :| && Enum.any?(matched_scopes) -> conn - op == :| && scopes -- token.scopes != scopes -> - conn - - op == :& && scopes -- token.scopes == [] -> + op == :& && matched_scopes == scopes -> conn options[:fallback] == :proceed_unauthenticated -> conn |> assign(:user, nil) |> assign(:token, nil) + |> maybe_perform_instance_privacy_check(options) true -> - missing_scopes = scopes -- token.scopes + missing_scopes = scopes -- matched_scopes permissions = Enum.join(missing_scopes, " #{op} ") error_message = @@ -42,4 +46,25 @@ def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do |> halt() end end + + @doc "Filters descendants of supported scopes" + def filter_descendants(scopes, supported_scopes) do + Enum.filter( + scopes, + fn scope -> + Enum.find( + supported_scopes, + &(scope == &1 || String.starts_with?(scope, &1 <> ":")) + ) + end + ) + end + + defp maybe_perform_instance_privacy_check(%Plug.Conn{} = conn, options) do + if options[:skip_instance_privacy_check] do + conn + else + EnsurePublicOrAuthenticatedPlug.call(conn, []) + end + end end diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index f20aeb0d5..1e7c9ae86 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -48,7 +48,7 @@ def refetch_public_key(conn) do end def sign(%User{} = user, headers) do - with {:ok, %{info: %{keys: keys}}} <- User.ensure_keys_present(user), + with {:ok, %{keys: keys}} <- User.ensure_keys_present(user), {:ok, private_key, _} <- Keys.keys_from_pem(keys) do HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers) end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index c2f8fa0d7..2cfb13a8c 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -11,6 +11,7 @@ defmodule Pleroma.User do alias Comeonin.Pbkdf2 alias Ecto.Multi alias Pleroma.Activity + alias Pleroma.Conversation.Participation alias Pleroma.Delivery alias Pleroma.Keys alias Pleroma.Notification @@ -50,6 +51,7 @@ defmodule Pleroma.User do field(:password_hash, :string) field(:password, :string, virtual: true) field(:password_confirmation, :string, virtual: true) + field(:keys, :string) field(:following, {:array, :string}, default: []) field(:ap_id, :string) field(:avatar, :map) @@ -842,6 +844,61 @@ def maybe_update_following_count(%User{local: false} = user) do def maybe_update_following_count(user), do: user + def set_unread_conversation_count(%User{local: true} = user) do + unread_query = Participation.unread_conversation_count_for_user(user) + + User + |> join(:inner, [u], p in subquery(unread_query)) + |> update([u, p], + set: [ + info: + fragment( + "jsonb_set(?, '{unread_conversation_count}', ?::varchar::jsonb, true)", + u.info, + p.count + ) + ] + ) + |> where([u], u.id == ^user.id) + |> select([u], u) + |> Repo.update_all([]) + |> case do + {1, [user]} -> set_cache(user) + _ -> {:error, user} + end + end + + def set_unread_conversation_count(_), do: :noop + + def increment_unread_conversation_count(conversation, %User{local: true} = user) do + unread_query = + Participation.unread_conversation_count_for_user(user) + |> where([p], p.conversation_id == ^conversation.id) + + User + |> join(:inner, [u], p in subquery(unread_query)) + |> update([u, p], + set: [ + info: + fragment( + "jsonb_set(?, '{unread_conversation_count}', (coalesce((?->>'unread_conversation_count')::int, 0) + 1)::varchar::jsonb, true)", + u.info, + u.info + ) + ] + ) + |> where([u], u.id == ^user.id) + |> where([u, p], p.count == 0) + |> select([u], u) + |> Repo.update_all([]) + |> case do + {1, [user]} -> set_cache(user) + _ -> {:error, user} + end + end + + def increment_unread_conversation_count(_, _), do: :noop + def remove_duplicated_following(%User{following: following} = user) do uniq_following = Enum.uniq(following) @@ -1498,11 +1555,14 @@ def get_mascot(%{info: %{mascot: mascot}}) when is_nil(mascot) do } end - def ensure_keys_present(%{info: %{keys: keys}} = user) when not is_nil(keys), do: {:ok, user} + def ensure_keys_present(%{keys: keys} = user) when not is_nil(keys), do: {:ok, user} def ensure_keys_present(%User{} = user) do with {:ok, pem} <- Keys.generate_rsa_pem() do - update_info(user, &User.Info.set_keys(&1, pem)) + user + |> cast(%{keys: pem}, [:keys]) + |> validate_required([:keys]) + |> update_and_set_cache() end end diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index ebd4ddebf..4b5b43d7f 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -47,6 +47,7 @@ defmodule Pleroma.User.Info do field(:hide_followers, :boolean, default: false) field(:hide_follows, :boolean, default: false) field(:hide_favorites, :boolean, default: true) + field(:unread_conversation_count, :integer, default: 0) field(:pinned_activities, {:array, :string}, default: []) field(:email_notifications, :map, default: %{"digest" => false}) field(:mascot, :map, default: nil) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 5052d1304..9f29087df 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -780,8 +780,8 @@ defp restrict_media(query, _), do: query defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do from( - activity in query, - where: fragment("?->'object'->>'inReplyTo' is null", activity.data) + [_activity, object] in query, + where: fragment("?->>'inReplyTo' is null", object.data) ) end diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 8aa6852f0..8e53296e7 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -168,7 +168,9 @@ def filter(%{"id" => actor, "type" => obj_type} = object) when obj_type in ["Application", "Group", "Organization", "Person", "Service"] do actor_info = URI.parse(actor) - with {:ok, object} <- check_avatar_removal(actor_info, object), + with {:ok, object} <- check_accept(actor_info, object), + {:ok, object} <- check_reject(actor_info, object), + {:ok, object} <- check_avatar_removal(actor_info, object), {:ok, object} <- check_banner_removal(actor_info, object) do {:ok, object} else diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index c56d2dd11..872ed0eb2 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -580,7 +580,7 @@ def handle_incoming( ) do with actor <- Containment.get_actor(data), {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- get_obj_helper(object_id), + {:ok, object} <- get_embedded_obj_helper(object_id, actor), public <- Visibility.is_public?(data), {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do {:ok, activity} @@ -782,6 +782,29 @@ def get_obj_helper(id, options \\ []) do end end + @spec get_embedded_obj_helper(String.t() | Object.t(), User.t()) :: {:ok, Object.t()} | nil + def get_embedded_obj_helper(%{"attributedTo" => attributed_to, "id" => object_id} = data, %User{ + ap_id: ap_id + }) + when attributed_to == ap_id do + with {:ok, activity} <- + handle_incoming(%{ + "type" => "Create", + "to" => data["to"], + "cc" => data["cc"], + "actor" => attributed_to, + "object" => data + }) do + {:ok, Object.normalize(activity)} + else + _ -> get_obj_helper(object_id) + end + end + + def get_embedded_obj_helper(object_id, _) do + get_obj_helper(object_id) + end + def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) when is_binary(in_reply_to) do with false <- String.starts_with?(in_reply_to, "http"), {:ok, %{data: replied_to_object}} <- get_obj_helper(in_reply_to) do diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 6bc55c85b..9b39d1629 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -33,7 +33,7 @@ def render("endpoints.json", _), do: %{} def render("service.json", %{user: user}) do {:ok, user} = User.ensure_keys_present(user) - {:ok, _, public_key} = Keys.keys_from_pem(user.info.keys) + {:ok, _, public_key} = Keys.keys_from_pem(user.keys) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_encode([public_key]) @@ -69,7 +69,7 @@ def render("user.json", %{user: %User{nickname: "internal." <> _} = user}), def render("user.json", %{user: user}) do {:ok, user} = User.ensure_keys_present(user) - {:ok, _, public_key} = Keys.keys_from_pem(user.info.keys) + {:ok, _, public_key} = Keys.keys_from_pem(user.keys) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_encode([public_key]) diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 21da8a7ff..513bae800 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do use Pleroma.Web, :controller alias Pleroma.Activity alias Pleroma.ModerationLog + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.UserInviteToken alias Pleroma.Web.ActivityPub.ActivityPub @@ -26,6 +27,67 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do require Logger + plug( + OAuthScopesPlug, + %{scopes: ["read:accounts"]} + when action in [:list_users, :user_show, :right_get, :invites] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} + when action in [ + :get_invite_token, + :revoke_invite, + :email_invite, + :get_password_reset, + :user_follow, + :user_unfollow, + :user_delete, + :users_create, + :user_toggle_activation, + :tag_users, + :untag_users, + :right_add, + :right_delete, + :set_activation_status + ] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read:reports"]} when action in [:list_reports, :report_show] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:reports"]} + when action in [:report_update_state, :report_respond] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read:statuses"]} when action == :list_user_statuses + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:statuses"]} + when action in [:status_update, :status_delete] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read"]} + when action in [:config_show, :migrate_to_db, :migrate_from_db, :list_log] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write"]} + when action in [:relay_follow, :relay_unfollow, :config_update] + ) + @users_page_size 50 action_fallback(:errors) diff --git a/lib/pleroma/web/feed/feed_controller.ex b/lib/pleroma/web/feed/feed_controller.ex new file mode 100644 index 000000000..d91ecef9c --- /dev/null +++ b/lib/pleroma/web/feed/feed_controller.ex @@ -0,0 +1,63 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Feed.FeedController do + use Pleroma.Web, :controller + + alias Fallback.RedirectController + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.ActivityPubController + + plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect]) + + action_fallback(:errors) + + def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do + with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do + RedirectController.redirector_with_meta(conn, %{user: user}) + end + end + + def feed_redirect(%{assigns: %{format: format}} = conn, _params) + when format in ["json", "activity+json"] do + ActivityPubController.call(conn, :user) + end + + def feed_redirect(conn, %{"nickname" => nickname}) do + with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do + redirect(conn, external: "#{feed_url(conn, :feed, user.nickname)}.atom") + end + end + + def feed(conn, %{"nickname" => nickname} = params) do + with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do + query_params = + params + |> Map.take(["max_id"]) + |> Map.put("type", ["Create"]) + |> Map.put("whole_db", true) + |> Map.put("actor_id", user.ap_id) + + activities = + query_params + |> ActivityPub.fetch_public_activities() + |> Enum.reverse() + + conn + |> put_resp_content_type("application/atom+xml") + |> render("feed.xml", user: user, activities: activities) + end + end + + def errors(conn, {:error, :not_found}) do + render_error(conn, :not_found, "Not found") + end + + def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found}) + + def errors(conn, _) do + render_error(conn, :internal_server_error, "Something went wrong") + end +end diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex new file mode 100644 index 000000000..5eef1e757 --- /dev/null +++ b/lib/pleroma/web/feed/feed_view.ex @@ -0,0 +1,77 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Feed.FeedView do + use Phoenix.HTML + use Pleroma.Web, :view + + alias Pleroma.Object + alias Pleroma.User + alias Pleroma.Web.MediaProxy + + require Pleroma.Constants + + def most_recent_update(activities, user) do + (List.first(activities) || user).updated_at + |> NaiveDateTime.to_iso8601() + end + + def logo(user) do + user + |> User.avatar_url() + |> MediaProxy.url() + end + + def last_activity(activities) do + List.last(activities) + end + + def activity_object(activity) do + Object.normalize(activity) + end + + def activity_object_data(activity) do + activity + |> activity_object() + |> Map.get(:data) + end + + def activity_content(activity) do + content = activity_object_data(activity)["content"] + + content + |> String.replace(~r/[\n\r]/, "") + |> escape() + end + + def activity_context(activity) do + activity.data["context"] + end + + def attachment_href(attachment) do + attachment["url"] + |> hd() + |> Map.get("href") + end + + def attachment_type(attachment) do + attachment["url"] + |> hd() + |> Map.get("mediaType") + end + + def get_href(id) do + with %Object{data: %{"external_url" => external_url}} <- Object.get_cached_by_ap_id(id) do + external_url + else + _e -> id + end + end + + def escape(html) do + html + |> html_escape() + |> safe_to_string() + end +end diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex index ac9af7502..87860f1d5 100644 --- a/lib/pleroma/web/masto_fe_controller.ex +++ b/lib/pleroma/web/masto_fe_controller.ex @@ -5,8 +5,20 @@ defmodule Pleroma.Web.MastoFEController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User + plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :put_settings) + + # Note: :index action handles attempt of unauthenticated access to private instance with redirect + plug( + OAuthScopesPlug, + %{scopes: ["read"], fallback: :proceed_unauthenticated, skip_instance_privacy_check: true} + when action == :index + ) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index) + @doc "GET /web/*path" def index(%{assigns: %{user: user}} = conn, _params) do token = get_session(conn, :oauth_token) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index df14ad66f..9ef7fd48d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do only: [add_link_headers: 2, truthy_param?: 1, assign_account_by_id: 2, json_response: 3] alias Pleroma.Emoji + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.RateLimiter alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub @@ -19,6 +20,49 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do alias Pleroma.Web.OAuth.Token alias Pleroma.Web.TwitterAPI.TwitterAPI + plug( + OAuthScopesPlug, + %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]} + when action == :show + ) + + plug( + OAuthScopesPlug, + %{scopes: ["read:accounts"]} + when action in [:endorsements, :verify_credentials, :followers, :following] + ) + + plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :update_credentials) + + plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists) + + plug( + OAuthScopesPlug, + %{scopes: ["follow", "read:blocks"]} when action == :blocks + ) + + plug( + OAuthScopesPlug, + %{scopes: ["follow", "write:blocks"]} when action in [:block, :unblock] + ) + + plug(OAuthScopesPlug, %{scopes: ["read:follows"]} when action == :relationships) + + # Note: :follows (POST /api/v1/follows) is the same as :follow, consider removing :follows + plug( + OAuthScopesPlug, + %{scopes: ["follow", "write:follows"]} when action in [:follows, :follow, :unfollow] + ) + + plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes) + + plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute]) + + plug( + Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + when action != :create + ) + @relations [:follow, :unfollow] @needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a @@ -105,6 +149,17 @@ def update_credentials(%{assigns: %{user: original_user}} = conn, params) do |> Enum.concat(Emoji.Formatter.get_emoji_map(emojis_text)) |> Enum.dedup() + params = + if Map.has_key?(params, "fields_attributes") do + Map.update!(params, "fields_attributes", fn fields -> + fields + |> normalize_fields_attributes() + |> Enum.filter(fn %{"name" => n} -> n != "" end) + end) + else + params + end + info_params = [ :no_rich_text, @@ -122,12 +177,12 @@ def update_credentials(%{assigns: %{user: original_user}} = conn, params) do add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)}) end) |> add_if_present(params, "default_scope", :default_scope) - |> add_if_present(params, "fields", :fields, fn fields -> + |> add_if_present(params, "fields_attributes", :fields, fn fields -> fields = Enum.map(fields, fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end) {:ok, fields} end) - |> add_if_present(params, "fields", :raw_fields) + |> add_if_present(params, "fields_attributes", :raw_fields) |> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store, fn value -> {:ok, Map.merge(user.info.pleroma_settings_store, value)} end) @@ -168,6 +223,14 @@ defp add_if_present(map, params, params_field, map_field, value_function \\ &{:o end end + defp normalize_fields_attributes(fields) do + if Enum.all?(fields, &is_tuple/1) do + Enum.map(fields, fn {_, v} -> v end) + else + fields + end + end + @doc "GET /api/v1/accounts/relationships" def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do targets = User.get_all_by_ids(List.wrap(id)) @@ -301,4 +364,30 @@ def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do {:error, message} -> json_response(conn, :forbidden, %{error: message}) end end + + @doc "POST /api/v1/follows" + def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do + with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)}, + {_, true} <- {:followed, follower.id != followed.id}, + {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do + render(conn, "show.json", user: followed, for: follower) + else + {:followed, _} -> {:error, :not_found} + {:error, message} -> json_response(conn, :forbidden, %{error: message}) + end + end + + @doc "GET /api/v1/mutes" + def mutes(%{assigns: %{user: user}} = conn, _) do + render(conn, "index.json", users: User.muted_users(user), for: user, as: :user) + end + + @doc "GET /api/v1/blocks" + def blocks(%{assigns: %{user: user}} = conn, _) do + render(conn, "index.json", users: User.blocked_users(user), for: user, as: :user) + end + + @doc "GET /api/v1/endorsements" + def endorsements(conn, params), + do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params) end diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index abbe16a88..13a30a34d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.AppController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Scopes @@ -12,6 +13,8 @@ defmodule Pleroma.Web.MastodonAPI.AppController do action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials) + @local_mastodon_name "Mastodon-Local" @doc "POST /api/v1/apps" diff --git a/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex b/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex index ea1e36a12..6c0584c54 100644 --- a/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex @@ -8,10 +8,16 @@ defmodule Pleroma.Web.MastodonAPI.ConversationController do import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] alias Pleroma.Conversation.Participation + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action == :index) + plug(OAuthScopesPlug, %{scopes: ["write:conversations"]} when action == :read) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "GET /api/v1/conversations" def index(%{assigns: %{user: user}} = conn, params) do participations = Participation.for_user_with_last_activity_id(user, params) diff --git a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex index 03db6c9b8..c7606246b 100644 --- a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex @@ -5,8 +5,21 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User + plug( + OAuthScopesPlug, + %{scopes: ["follow", "read:blocks"]} when action == :index + ) + + plug( + OAuthScopesPlug, + %{scopes: ["follow", "write:blocks"]} when action != :index + ) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "GET /api/v1/domain_blocks" def index(%{assigns: %{user: %{info: info}}} = conn, _) do json(conn, Map.get(info, :domain_blocks, [])) diff --git a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex index 19041304e..cadef72e1 100644 --- a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex @@ -6,6 +6,18 @@ defmodule Pleroma.Web.MastodonAPI.FilterController do use Pleroma.Web, :controller alias Pleroma.Filter + alias Pleroma.Plugs.OAuthScopesPlug + + @oauth_read_actions [:show, :index] + + plug(OAuthScopesPlug, %{scopes: ["read:filters"]} when action in @oauth_read_actions) + + plug( + OAuthScopesPlug, + %{scopes: ["write:filters"]} when action not in @oauth_read_actions + ) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) @doc "GET /api/v1/filters" def index(%{assigns: %{user: user}} = conn, _) do diff --git a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex index ce7b625ee..3ccbdf1c6 100644 --- a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.CommonAPI @@ -13,6 +14,15 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do action_fallback(:errors) + plug(OAuthScopesPlug, %{scopes: ["follow", "read:follows"]} when action == :index) + + plug( + OAuthScopesPlug, + %{scopes: ["follow", "write:follows"]} when action != :index + ) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "GET /api/v1/follow_requests" def index(%{assigns: %{user: followed}} = conn, _params) do follow_requests = User.get_follow_requests(followed) diff --git a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex index 50f42bee5..e0ffdba21 100644 --- a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex @@ -5,11 +5,22 @@ defmodule Pleroma.Web.MastodonAPI.ListController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.MastodonAPI.AccountView plug(:list_by_id_and_user when action not in [:index, :create]) + plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in [:index, :show, :list_accounts]) + + plug( + OAuthScopesPlug, + %{scopes: ["write:lists"]} + when action in [:create, :update, :delete, :add_to_list, :remove_from_list] + ) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) # GET /api/v1/lists diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index e92f5d089..7d839a8cf 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -5,86 +5,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do use Pleroma.Web, :controller - import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] - - alias Pleroma.Bookmark - alias Pleroma.Pagination - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.MastodonAPI.AccountView - alias Pleroma.Web.MastodonAPI.StatusView - require Logger action_fallback(Pleroma.Web.MastodonAPI.FallbackController) - def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do - with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)}, - {_, true} <- {:followed, follower.id != followed.id}, - {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do - conn - |> put_view(AccountView) - |> render("show.json", %{user: followed, for: follower}) - else - {:followed, _} -> - {:error, :not_found} - - {:error, message} -> - conn - |> put_status(:forbidden) - |> json(%{error: message}) - end - end - - def mutes(%{assigns: %{user: user}} = conn, _) do - with muted_accounts <- User.muted_users(user) do - res = AccountView.render("index.json", users: muted_accounts, for: user, as: :user) - json(conn, res) - end - end - - def blocks(%{assigns: %{user: user}} = conn, _) do - with blocked_accounts <- User.blocked_users(user) do - res = AccountView.render("index.json", users: blocked_accounts, for: user, as: :user) - json(conn, res) - end - end - - def favourites(%{assigns: %{user: user}} = conn, params) do - params = - params - |> Map.put("type", "Create") - |> Map.put("favorited_by", user.ap_id) - |> Map.put("blocking_user", user) - - activities = - ActivityPub.fetch_activities([], params) - |> Enum.reverse() - - conn - |> add_link_headers(activities) - |> put_view(StatusView) - |> render("index.json", %{activities: activities, for: user, as: :activity}) - end - - def bookmarks(%{assigns: %{user: user}} = conn, params) do - user = User.get_cached_by_id(user.id) - - bookmarks = - Bookmark.for_user_query(user.id) - |> Pagination.fetch_paginated(params) - - activities = - bookmarks - |> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end) - - conn - |> add_link_headers(bookmarks) - |> put_view(StatusView) - |> render("index.json", %{activities: activities, for: user, as: :activity}) - end - # Stubs for unimplemented mastodon api # def empty_array(conn, _) do diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex index 57a5b60fb..ed4c08d99 100644 --- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex @@ -6,12 +6,17 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do use Pleroma.Web, :controller alias Pleroma.Object + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(:put_view, Pleroma.Web.MastodonAPI.StatusView) + plug(OAuthScopesPlug, %{scopes: ["write:media"]}) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "POST /api/v1/media" def create(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do with {:ok, object} <- diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index 7e4d7297c..16759be6a 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -8,8 +8,20 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] alias Pleroma.Notification + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.MastodonAPI.MastodonAPI + @oauth_read_actions [:show, :index] + + plug( + OAuthScopesPlug, + %{scopes: ["read:notifications"]} when action in @oauth_read_actions + ) + + plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action not in @oauth_read_actions) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + # GET /api/v1/notifications def index(%{assigns: %{user: user}} = conn, params) do notifications = MastodonAPI.get_notifications(user, params) diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index fbf7f8673..d129f8672 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -9,11 +9,21 @@ defmodule Pleroma.Web.MastodonAPI.PollController do alias Pleroma.Activity alias Pleroma.Object + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.CommonAPI action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + plug( + OAuthScopesPlug, + %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated} when action == :show + ) + + plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :vote) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "GET /api/v1/polls/:id" def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60), diff --git a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex index 1c084b740..263c2180f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex @@ -3,10 +3,16 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ReportController do + alias Pleroma.Plugs.OAuthScopesPlug + use Pleroma.Web, :controller action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + plug(OAuthScopesPlug, %{scopes: ["write:reports"]} when action == :create) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "POST /api/v1/reports" def create(%{assigns: %{user: user}} = conn, params) do with {:ok, activity} <- Pleroma.Web.CommonAPI.report(user, params) do diff --git a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex index 0a56b10b6..ff9276541 100644 --- a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex @@ -7,11 +7,19 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.ScheduledActivity alias Pleroma.Web.MastodonAPI.MastodonAPI plug(:assign_scheduled_activity when action != :index) + @oauth_read_actions [:show, :index] + + plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in @oauth_read_actions) + plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action not in @oauth_read_actions) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) @doc "GET /api/v1/scheduled_statuses" diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index 3fc89d645..6cfd68a84 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do use Pleroma.Web, :controller alias Pleroma.Activity + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.RateLimiter alias Pleroma.Repo alias Pleroma.User @@ -15,6 +16,12 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do alias Pleroma.Web.MastodonAPI.StatusView require Logger + + # Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search) + plug(OAuthScopesPlug, %{scopes: ["read:search"], fallback: :proceed_unauthenticated}) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + plug(RateLimiter, :search when action in [:search, :search2, :account_search]) def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 79cced163..e5d016f63 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -5,13 +5,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do use Pleroma.Web, :controller - import Pleroma.Web.ControllerHelper, only: [try_render: 3] + import Pleroma.Web.ControllerHelper, only: [try_render: 3, add_link_headers: 2] require Ecto.Query alias Pleroma.Activity alias Pleroma.Bookmark alias Pleroma.Object + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.RateLimiter alias Pleroma.Repo alias Pleroma.ScheduledActivity @@ -22,6 +23,61 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.ScheduledActivityView + @unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []} + + plug( + OAuthScopesPlug, + %{@unauthenticated_access | scopes: ["read:statuses"]} + when action in [ + :index, + :show, + :card, + :context + ] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:statuses"]} + when action in [ + :create, + :delete, + :reblog, + :unreblog + ] + ) + + plug(OAuthScopesPlug, %{scopes: ["read:favourites"]} when action == :favourites) + + plug( + OAuthScopesPlug, + %{scopes: ["write:favourites"]} when action in [:favourite, :unfavourite] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:mutes"]} when action in [:mute_conversation, :unmute_conversation] + ) + + plug( + OAuthScopesPlug, + %{@unauthenticated_access | scopes: ["read:accounts"]} + when action in [:favourited_by, :reblogged_by] + ) + + plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action in [:pin, :unpin]) + + # Note: scope not present in Mastodon: read:bookmarks + plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :bookmarks) + + # Note: scope not present in Mastodon: write:bookmarks + plug( + OAuthScopesPlug, + %{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark] + ) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete)a plug( @@ -111,7 +167,11 @@ def create(%{assigns: %{user: _user}} = conn, %{"media_ids" => _} = params) do def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.visible_for_user?(activity, user) do - try_render(conn, "show.json", activity: activity, for: user) + try_render(conn, "show.json", + activity: activity, + for: user, + with_direct_conversation_id: true + ) end end @@ -283,4 +343,39 @@ def context(%{assigns: %{user: user}} = conn, %{"id" => id}) do render(conn, "context.json", activity: activity, activities: activities, user: user) end end + + @doc "GET /api/v1/favourites" + def favourites(%{assigns: %{user: user}} = conn, params) do + params = + params + |> Map.put("type", "Create") + |> Map.put("favorited_by", user.ap_id) + |> Map.put("blocking_user", user) + + activities = + ActivityPub.fetch_activities([], params) + |> Enum.reverse() + + conn + |> add_link_headers(activities) + |> render("index.json", activities: activities, for: user, as: :activity) + end + + @doc "GET /api/v1/bookmarks" + def bookmarks(%{assigns: %{user: user}} = conn, params) do + user = User.get_cached_by_id(user.id) + + bookmarks = + user.id + |> Bookmark.for_user_query() + |> Pleroma.Pagination.fetch_paginated(params) + + activities = + bookmarks + |> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end) + + conn + |> add_link_headers(bookmarks) + |> render("index.json", %{activities: activities, for: user, as: :activity}) + end end diff --git a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex index e2b17aab1..fc7d52824 100644 --- a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex @@ -12,6 +12,10 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do action_fallback(:errors) + plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]}) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + # Creates PushSubscription # POST /api/v1/push/subscription # diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index 9076bb849..fe71c36af 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -8,11 +8,16 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do require Logger alias Pleroma.Config + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.MediaProxy action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :index) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "GET /api/v1/suggestions" def index(%{assigns: %{user: user}} = conn, _) do if Config.get([:suggestions, :enabled], false) do diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index bb8b0eb32..9f086a8c2 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -9,8 +9,14 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1] alias Pleroma.Pagination + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.ActivityPub.ActivityPub + plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct]) + plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + plug(:put_view, Pleroma.Web.MastodonAPI.StatusView) # GET /api/v1/timelines/home diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 99169ef95..2d4976891 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -167,6 +167,7 @@ defp do_render("show.json", %{user: user} = opts) do |> maybe_put_chat_token(user, opts[:for], opts) |> maybe_put_activation_status(user, opts[:for]) |> maybe_put_follow_requests_count(user, opts[:for]) + |> maybe_put_unread_conversation_count(user, opts[:for]) end defp username_from_nickname(string) when is_binary(string) do @@ -248,6 +249,16 @@ defp maybe_put_activation_status(data, user, %User{info: %{is_admin: true}}) do defp maybe_put_activation_status(data, _, _), do: data + defp maybe_put_unread_conversation_count(data, %User{id: user_id} = user, %User{id: user_id}) do + data + |> Kernel.put_in( + [:pleroma, :unread_conversation_count], + user.info.unread_conversation_count + ) + end + + defp maybe_put_unread_conversation_count(data, _, _), do: data + defp image_url(%{"url" => [%{"href" => href} | _]}), do: href defp image_url(_), do: nil end diff --git a/lib/pleroma/web/metadata/feed.ex b/lib/pleroma/web/metadata/feed.ex new file mode 100644 index 000000000..8043e6c54 --- /dev/null +++ b/lib/pleroma/web/metadata/feed.ex @@ -0,0 +1,23 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Providers.Feed do + alias Pleroma.Web.Endpoint + alias Pleroma.Web.Metadata.Providers.Provider + alias Pleroma.Web.Router.Helpers + + @behaviour Provider + + @impl Provider + def build_tags(%{user: user}) do + [ + {:link, + [ + rel: "alternate", + type: "application/atom+xml", + href: Helpers.feed_path(Endpoint, :feed, user.nickname) <> ".atom" + ], []} + ] + end +end diff --git a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex index b786a521b..6ed181cff 100644 --- a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex +++ b/lib/pleroma/web/mongooseim/mongoose_im_controller.ex @@ -4,10 +4,15 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do use Pleroma.Web, :controller + alias Comeonin.Pbkdf2 + alias Pleroma.Plugs.RateLimiter alias Pleroma.Repo alias Pleroma.User + plug(RateLimiter, :authentication when action in [:user_exists, :check_password]) + plug(RateLimiter, {:authentication, params: ["user"]} when action == :check_password) + def user_exists(conn, %{"user" => username}) do with %User{} <- Repo.get_by(User, nickname: username, local: true) do conn diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 1cd7294e7..03c9a5027 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -24,6 +24,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do plug(:fetch_session) plug(:fetch_flash) + plug(Pleroma.Plugs.RateLimiter, :authentication when action == :create_authorization) action_fallback(Pleroma.Web.OAuth.FallbackController) @@ -474,7 +475,7 @@ defp put_session_registration_id(%Plug.Conn{} = conn, registration_id), defp validate_scopes(app, params) do params |> Scopes.fetch_scopes(app.scopes) - |> Scopes.validates(app.scopes) + |> Scopes.validate(app.scopes) end def default_redirect_uri(%App{} = app) do diff --git a/lib/pleroma/web/oauth/scopes.ex b/lib/pleroma/web/oauth/scopes.ex index ad9dfb260..48bd14407 100644 --- a/lib/pleroma/web/oauth/scopes.ex +++ b/lib/pleroma/web/oauth/scopes.ex @@ -8,7 +8,7 @@ defmodule Pleroma.Web.OAuth.Scopes do """ @doc """ - Fetch scopes from requiest params. + Fetch scopes from request params. Note: `scopes` is used by Mastodon — supporting it but sticking to OAuth's standard `scope` wherever we control it @@ -53,14 +53,14 @@ def to_string(scopes), do: Enum.join(scopes, " ") @doc """ Validates scopes. """ - @spec validates(list() | nil, list()) :: + @spec validate(list() | nil, list()) :: {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes} - def validates([], _app_scopes), do: {:error, :missing_scopes} - def validates(nil, _app_scopes), do: {:error, :missing_scopes} + def validate([], _app_scopes), do: {:error, :missing_scopes} + def validate(nil, _app_scopes), do: {:error, :missing_scopes} - def validates(scopes, app_scopes) do - case scopes -- app_scopes do - [] -> {:ok, scopes} + def validate(scopes, app_scopes) do + case Pleroma.Plugs.OAuthScopesPlug.filter_descendants(scopes, app_scopes) do + ^scopes -> {:ok, scopes} _ -> {:error, :unsupported_scopes} end end diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex index 8f325b28e..20f2d9ddc 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -9,16 +9,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do alias Pleroma.Activity alias Pleroma.Object alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPubController alias Pleroma.Web.ActivityPub.ObjectView alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Endpoint alias Pleroma.Web.Federator alias Pleroma.Web.Metadata.PlayerView - alias Pleroma.Web.OStatus alias Pleroma.Web.OStatus.ActivityRepresenter - alias Pleroma.Web.OStatus.FeedRepresenter alias Pleroma.Web.Router alias Pleroma.Web.XML @@ -31,49 +28,11 @@ defmodule Pleroma.Web.OStatus.OStatusController do plug( Pleroma.Plugs.SetFormatPlug - when action in [:feed_redirect, :object, :activity, :notice] + when action in [:object, :activity, :notice] ) action_fallback(:errors) - def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do - with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do - RedirectController.redirector_with_meta(conn, %{user: user}) - end - end - - def feed_redirect(%{assigns: %{format: format}} = conn, _params) - when format in ["json", "activity+json"] do - ActivityPubController.call(conn, :user) - end - - def feed_redirect(conn, %{"nickname" => nickname}) do - with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do - redirect(conn, external: OStatus.feed_path(user)) - end - end - - def feed(conn, %{"nickname" => nickname} = params) do - with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do - activities = - params - |> Map.take(["max_id"]) - |> Map.merge(%{"whole_db" => true, "actor_id" => user.ap_id}) - |> ActivityPub.fetch_public_activities() - |> Enum.reverse() - - response = - user - |> FeedRepresenter.to_simple_form(activities, [user]) - |> :xmerl.export_simple(:xmerl_xml) - |> to_string - - conn - |> put_resp_content_type("application/atom+xml") - |> send_resp(200, response) - end - end - defp decode_or_retry(body) do with {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body), {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index 63c44086c..9012e2175 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2] alias Ecto.Changeset + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.RateLimiter alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub @@ -17,6 +18,30 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do require Pleroma.Constants + plug( + OAuthScopesPlug, + %{scopes: ["follow", "write:follows"]} when action in [:subscribe, :unsubscribe] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} + # Note: the following actions are not permission-secured in Mastodon: + when action in [ + :update_avatar, + :update_banner, + :update_background + ] + ) + + plug(OAuthScopesPlug, %{scopes: ["read:favourites"]} when action == :favourites) + + # An extra safety measure for possible actions not guarded by OAuth permissions specification + plug( + Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + when action != :confirmation_resend + ) + plug(RateLimiter, :account_confirmation_resend when action == :confirmation_resend) plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe]) plug(:put_view, Pleroma.Web.MastodonAPI.AccountView) diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index 545ad80c9..a474d41d4 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -1,8 +1,26 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug + require Logger + plug( + OAuthScopesPlug, + %{scopes: ["write"]} + when action in [ + :create, + :delete, + :download_from, + :list_from, + :import_from_fs, + :update_file, + :update_metadata + ] + ) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + def emoji_dir_path do Path.join( Pleroma.Config.get!([:instance, :static_dir]), diff --git a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex index 7f6a76c0e..d71d72dd5 100644 --- a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex @@ -5,9 +5,15 @@ defmodule Pleroma.Web.PleromaAPI.MascotController do use Pleroma.Web, :controller + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action == :show) + plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action != :show) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + @doc "GET /api/v1/pleroma/mascot" def show(%{assigns: %{user: user}} = conn, _params) do json(conn, User.get_mascot(user)) diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index d17ccf84d..9d50a7ca9 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -9,11 +9,26 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do alias Pleroma.Conversation.Participation alias Pleroma.Notification + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.MastodonAPI.ConversationView alias Pleroma.Web.MastodonAPI.NotificationView alias Pleroma.Web.MastodonAPI.StatusView + plug( + OAuthScopesPlug, + %{scopes: ["read:statuses"]} when action in [:conversation, :conversation_statuses] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:conversations"]} when action == :update_conversation + ) + + plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :read_notification) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do with %Participation{} = participation <- Participation.get(participation_id), true <- user.id == participation.user_id do diff --git a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex index 0fb978c5d..b74b3debc 100644 --- a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex @@ -7,11 +7,17 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleController do import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, fetch_integer_param: 2] + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.StatusView + plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :user_scrobbles) + plug(OAuthScopesPlug, %{scopes: ["write"]} when action != :user_scrobbles) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + def new_scrobble(%{assigns: %{user: user}} = conn, %{"title" => _} = params) do params = if !params["length"] do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 405ae724e..ae799b8ac 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -87,31 +87,6 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Plugs.EnsureUserKeyPlug) end - pipeline :oauth_read_or_public do - plug(Pleroma.Plugs.OAuthScopesPlug, %{ - scopes: ["read"], - fallback: :proceed_unauthenticated - }) - - plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) - end - - pipeline :oauth_read do - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["read"]}) - end - - pipeline :oauth_write do - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["write"]}) - end - - pipeline :oauth_follow do - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["follow"]}) - end - - pipeline :oauth_push do - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]}) - end - pipeline :well_known do plug(:accepts, ["json", "jrd+json", "xml", "xrd+xml"]) end @@ -154,7 +129,7 @@ defmodule Pleroma.Web.Router do end scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do - pipe_through([:admin_api, :oauth_write]) + pipe_through(:admin_api) post("/users/follow", AdminAPIController, :user_follow) post("/users/unfollow", AdminAPIController, :user_unfollow) @@ -213,7 +188,7 @@ defmodule Pleroma.Web.Router do scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do scope "/packs" do # Modifying packs - pipe_through([:admin_api, :oauth_write]) + pipe_through(:admin_api) post("/import_from_fs", EmojiAPIController, :import_from_fs) @@ -238,31 +213,20 @@ defmodule Pleroma.Web.Router do post("/main/ostatus", UtilController, :remote_subscribe) get("/ostatus_subscribe", UtilController, :remote_follow) - scope [] do - pipe_through(:oauth_follow) - post("/ostatus_subscribe", UtilController, :do_remote_follow) - end + post("/ostatus_subscribe", UtilController, :do_remote_follow) end scope "/api/pleroma", Pleroma.Web.TwitterAPI do pipe_through(:authenticated_api) - scope [] do - pipe_through(:oauth_write) + post("/change_email", UtilController, :change_email) + post("/change_password", UtilController, :change_password) + post("/delete_account", UtilController, :delete_account) + put("/notification_settings", UtilController, :update_notificaton_settings) + post("/disable_account", UtilController, :disable_account) - post("/change_email", UtilController, :change_email) - post("/change_password", UtilController, :change_password) - post("/delete_account", UtilController, :delete_account) - put("/notification_settings", UtilController, :update_notificaton_settings) - post("/disable_account", UtilController, :disable_account) - end - - scope [] do - pipe_through(:oauth_follow) - - post("/blocks_import", UtilController, :blocks_import) - post("/follow_import", UtilController, :follow_import) - end + post("/blocks_import", UtilController, :blocks_import) + post("/follow_import", UtilController, :follow_import) end scope "/oauth", Pleroma.Web.OAuth do @@ -289,14 +253,14 @@ defmodule Pleroma.Web.Router do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do scope [] do pipe_through(:authenticated_api) - pipe_through(:oauth_read) + get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses) get("/conversations/:id", PleromaAPIController, :conversation) end scope [] do pipe_through(:authenticated_api) - pipe_through(:oauth_write) + patch("/conversations/:id", PleromaAPIController, :update_conversation) post("/notifications/read", PleromaAPIController, :read_notification) @@ -312,13 +276,11 @@ defmodule Pleroma.Web.Router do scope [] do pipe_through(:api) - pipe_through(:oauth_read_or_public) get("/accounts/:id/favourites", AccountController, :favourites) end scope [] do pipe_through(:authenticated_api) - pipe_through(:oauth_follow) post("/accounts/:id/subscribe", AccountController, :subscribe) post("/accounts/:id/unsubscribe", AccountController, :unsubscribe) @@ -328,131 +290,114 @@ defmodule Pleroma.Web.Router do end scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do - pipe_through([:api, :oauth_read_or_public]) - + pipe_through(:api) get("/accounts/:id/scrobbles", ScrobbleController, :user_scrobbles) end scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:authenticated_api) - scope [] do - pipe_through(:oauth_read) + get("/accounts/verify_credentials", AccountController, :verify_credentials) - get("/accounts/verify_credentials", AccountController, :verify_credentials) + get("/accounts/relationships", AccountController, :relationships) - get("/accounts/relationships", AccountController, :relationships) + get("/accounts/:id/lists", AccountController, :lists) + get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) - get("/accounts/:id/lists", AccountController, :lists) - get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) + get("/follow_requests", FollowRequestController, :index) + get("/blocks", AccountController, :blocks) + get("/mutes", AccountController, :mutes) - get("/follow_requests", FollowRequestController, :index) - get("/blocks", MastodonAPIController, :blocks) - get("/mutes", MastodonAPIController, :mutes) + get("/timelines/home", TimelineController, :home) + get("/timelines/direct", TimelineController, :direct) - get("/timelines/home", TimelineController, :home) - get("/timelines/direct", TimelineController, :direct) + get("/favourites", StatusController, :favourites) + get("/bookmarks", StatusController, :bookmarks) - get("/favourites", MastodonAPIController, :favourites) - get("/bookmarks", MastodonAPIController, :bookmarks) + get("/notifications", NotificationController, :index) + get("/notifications/:id", NotificationController, :show) + post("/notifications/clear", NotificationController, :clear) + post("/notifications/dismiss", NotificationController, :dismiss) + delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple) - get("/notifications", NotificationController, :index) - get("/notifications/:id", NotificationController, :show) - post("/notifications/clear", NotificationController, :clear) - post("/notifications/dismiss", NotificationController, :dismiss) - delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple) + get("/scheduled_statuses", ScheduledActivityController, :index) + get("/scheduled_statuses/:id", ScheduledActivityController, :show) - get("/scheduled_statuses", ScheduledActivityController, :index) - get("/scheduled_statuses/:id", ScheduledActivityController, :show) + get("/lists", ListController, :index) + get("/lists/:id", ListController, :show) + get("/lists/:id/accounts", ListController, :list_accounts) - get("/lists", ListController, :index) - get("/lists/:id", ListController, :show) - get("/lists/:id/accounts", ListController, :list_accounts) + get("/domain_blocks", DomainBlockController, :index) - get("/domain_blocks", DomainBlockController, :index) + get("/filters", FilterController, :index) - get("/filters", FilterController, :index) + get("/suggestions", SuggestionController, :index) - get("/suggestions", SuggestionController, :index) + get("/conversations", ConversationController, :index) + post("/conversations/:id/read", ConversationController, :read) - get("/conversations", ConversationController, :index) - post("/conversations/:id/read", ConversationController, :read) + get("/endorsements", AccountController, :endorsements) - get("/endorsements", MastodonAPIController, :empty_array) - end + patch("/accounts/update_credentials", AccountController, :update_credentials) - scope [] do - pipe_through(:oauth_write) + post("/statuses", StatusController, :create) + delete("/statuses/:id", StatusController, :delete) - patch("/accounts/update_credentials", AccountController, :update_credentials) + post("/statuses/:id/reblog", StatusController, :reblog) + post("/statuses/:id/unreblog", StatusController, :unreblog) + post("/statuses/:id/favourite", StatusController, :favourite) + post("/statuses/:id/unfavourite", StatusController, :unfavourite) + post("/statuses/:id/pin", StatusController, :pin) + post("/statuses/:id/unpin", StatusController, :unpin) + post("/statuses/:id/bookmark", StatusController, :bookmark) + post("/statuses/:id/unbookmark", StatusController, :unbookmark) + post("/statuses/:id/mute", StatusController, :mute_conversation) + post("/statuses/:id/unmute", StatusController, :unmute_conversation) - post("/statuses", StatusController, :create) - delete("/statuses/:id", StatusController, :delete) + put("/scheduled_statuses/:id", ScheduledActivityController, :update) + delete("/scheduled_statuses/:id", ScheduledActivityController, :delete) - post("/statuses/:id/reblog", StatusController, :reblog) - post("/statuses/:id/unreblog", StatusController, :unreblog) - post("/statuses/:id/favourite", StatusController, :favourite) - post("/statuses/:id/unfavourite", StatusController, :unfavourite) - post("/statuses/:id/pin", StatusController, :pin) - post("/statuses/:id/unpin", StatusController, :unpin) - post("/statuses/:id/bookmark", StatusController, :bookmark) - post("/statuses/:id/unbookmark", StatusController, :unbookmark) - post("/statuses/:id/mute", StatusController, :mute_conversation) - post("/statuses/:id/unmute", StatusController, :unmute_conversation) + post("/polls/:id/votes", PollController, :vote) - put("/scheduled_statuses/:id", ScheduledActivityController, :update) - delete("/scheduled_statuses/:id", ScheduledActivityController, :delete) + post("/media", MediaController, :create) + put("/media/:id", MediaController, :update) - post("/polls/:id/votes", PollController, :vote) + delete("/lists/:id", ListController, :delete) + post("/lists", ListController, :create) + put("/lists/:id", ListController, :update) - post("/media", MediaController, :create) - put("/media/:id", MediaController, :update) + post("/lists/:id/accounts", ListController, :add_to_list) + delete("/lists/:id/accounts", ListController, :remove_from_list) - delete("/lists/:id", ListController, :delete) - post("/lists", ListController, :create) - put("/lists/:id", ListController, :update) + post("/filters", FilterController, :create) + get("/filters/:id", FilterController, :show) + put("/filters/:id", FilterController, :update) + delete("/filters/:id", FilterController, :delete) - post("/lists/:id/accounts", ListController, :add_to_list) - delete("/lists/:id/accounts", ListController, :remove_from_list) + post("/reports", ReportController, :create) - post("/filters", FilterController, :create) - get("/filters/:id", FilterController, :show) - put("/filters/:id", FilterController, :update) - delete("/filters/:id", FilterController, :delete) + post("/follows", AccountController, :follows) + post("/accounts/:id/follow", AccountController, :follow) + post("/accounts/:id/unfollow", AccountController, :unfollow) + post("/accounts/:id/block", AccountController, :block) + post("/accounts/:id/unblock", AccountController, :unblock) + post("/accounts/:id/mute", AccountController, :mute) + post("/accounts/:id/unmute", AccountController, :unmute) - post("/reports", ReportController, :create) - end + post("/follow_requests/:id/authorize", FollowRequestController, :authorize) + post("/follow_requests/:id/reject", FollowRequestController, :reject) - scope [] do - pipe_through(:oauth_follow) + post("/domain_blocks", DomainBlockController, :create) + delete("/domain_blocks", DomainBlockController, :delete) - post("/follows", MastodonAPIController, :follows) - post("/accounts/:id/follow", AccountController, :follow) - post("/accounts/:id/unfollow", AccountController, :unfollow) - post("/accounts/:id/block", AccountController, :block) - post("/accounts/:id/unblock", AccountController, :unblock) - post("/accounts/:id/mute", AccountController, :mute) - post("/accounts/:id/unmute", AccountController, :unmute) - - post("/follow_requests/:id/authorize", FollowRequestController, :authorize) - post("/follow_requests/:id/reject", FollowRequestController, :reject) - - post("/domain_blocks", DomainBlockController, :create) - delete("/domain_blocks", DomainBlockController, :delete) - end - - scope [] do - pipe_through(:oauth_push) - - post("/push/subscription", SubscriptionController, :create) - get("/push/subscription", SubscriptionController, :get) - put("/push/subscription", SubscriptionController, :update) - delete("/push/subscription", SubscriptionController, :delete) - end + post("/push/subscription", SubscriptionController, :create) + get("/push/subscription", SubscriptionController, :get) + put("/push/subscription", SubscriptionController, :update) + delete("/push/subscription", SubscriptionController, :delete) end scope "/api/web", Pleroma.Web do - pipe_through([:authenticated_api, :oauth_write]) + pipe_through(:authenticated_api) put("/settings", MastoFEController, :put_settings) end @@ -477,30 +422,26 @@ defmodule Pleroma.Web.Router do get("/trends", MastodonAPIController, :empty_array) - scope [] do - pipe_through(:oauth_read_or_public) + get("/timelines/public", TimelineController, :public) + get("/timelines/tag/:tag", TimelineController, :hashtag) + get("/timelines/list/:list_id", TimelineController, :list) - get("/timelines/public", TimelineController, :public) - get("/timelines/tag/:tag", TimelineController, :hashtag) - get("/timelines/list/:list_id", TimelineController, :list) + get("/statuses", StatusController, :index) + get("/statuses/:id", StatusController, :show) + get("/statuses/:id/context", StatusController, :context) - get("/statuses", StatusController, :index) - get("/statuses/:id", StatusController, :show) - get("/statuses/:id/context", StatusController, :context) + get("/polls/:id", PollController, :show) - get("/polls/:id", PollController, :show) + get("/accounts/:id/statuses", AccountController, :statuses) + get("/accounts/:id/followers", AccountController, :followers) + get("/accounts/:id/following", AccountController, :following) + get("/accounts/:id", AccountController, :show) - get("/accounts/:id/statuses", AccountController, :statuses) - get("/accounts/:id/followers", AccountController, :followers) - get("/accounts/:id/following", AccountController, :following) - get("/accounts/:id", AccountController, :show) - - get("/search", SearchController, :search) - end + get("/search", SearchController, :search) end scope "/api/v2", Pleroma.Web.MastodonAPI do - pipe_through([:api, :oauth_read_or_public]) + pipe_through(:api) get("/search", SearchController, :search2) end @@ -531,11 +472,7 @@ defmodule Pleroma.Web.Router do get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens) delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token) - scope [] do - pipe_through(:oauth_read) - - post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read) - end + post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read) end pipeline :ap_service_actor do @@ -558,8 +495,9 @@ defmodule Pleroma.Web.Router do get("/activities/:uuid", OStatus.OStatusController, :activity) get("/notice/:id", OStatus.OStatusController, :notice) get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player) - get("/users/:nickname/feed", OStatus.OStatusController, :feed) - get("/users/:nickname", OStatus.OStatusController, :feed_redirect) + + get("/users/:nickname/feed", Feed.FeedController, :feed) + get("/users/:nickname", Feed.FeedController, :feed_redirect) post("/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming) post("/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request) @@ -599,23 +537,14 @@ defmodule Pleroma.Web.Router do scope "/", Pleroma.Web.ActivityPub do pipe_through([:activitypub_client]) - scope [] do - pipe_through(:oauth_read) - get("/api/ap/whoami", ActivityPubController, :whoami) - get("/users/:nickname/inbox", ActivityPubController, :read_inbox) - end + get("/api/ap/whoami", ActivityPubController, :whoami) + get("/users/:nickname/inbox", ActivityPubController, :read_inbox) - scope [] do - pipe_through(:oauth_write) - post("/users/:nickname/outbox", ActivityPubController, :update_outbox) - post("/api/ap/upload_media", ActivityPubController, :upload_media) - end + post("/users/:nickname/outbox", ActivityPubController, :update_outbox) + post("/api/ap/upload_media", ActivityPubController, :upload_media) - scope [] do - pipe_through(:oauth_read_or_public) - get("/users/:nickname/followers", ActivityPubController, :followers) - get("/users/:nickname/following", ActivityPubController, :following) - end + get("/users/:nickname/followers", ActivityPubController, :followers) + get("/users/:nickname/following", ActivityPubController, :following) end scope "/", Pleroma.Web.ActivityPub do @@ -665,10 +594,7 @@ defmodule Pleroma.Web.Router do post("/auth/password", MastodonAPI.AuthController, :password_reset) - scope [] do - pipe_through(:oauth_read) - get("/web/*path", MastoFEController, :index) - end + get("/web/*path", MastoFEController, :index) end pipeline :remote_media do diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 8ba7380c0..0ffe903cd 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -202,7 +202,7 @@ def is_representable?(_), do: false @spec publish(User.t(), Pleroma.Activity.t()) :: none def publish(user, activity) - def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity) + def publish(%{keys: keys} = user, %{data: %{"type" => type}} = activity) when type in @supported_activities do feed = ActivityRepresenter.to_simple_form(activity, user, true) @@ -238,7 +238,7 @@ def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end) def gather_webfinger_links(%User{} = user) do - {:ok, _private, public} = Keys.keys_from_pem(user.info.keys) + {:ok, _private, public} = Keys.keys_from_pem(user.keys) magic_key = encode_key(public) [ diff --git a/lib/pleroma/web/templates/feed/feed/_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex new file mode 100644 index 000000000..d1f5e903c --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/_activity.xml.eex @@ -0,0 +1,48 @@ + + http://activitystrea.ms/schema/1.0/note + http://activitystrea.ms/schema/1.0/post + <%= @data["id"] %> + <%= "New note by #{@user.nickname}" %> + <%= activity_content(@activity) %> + <%= @data["published"] %> + <%= @data["published"] %> + <%= activity_context(@activity) %> + + + <%= if @data["summary"] do %> + <%= @data["summary"] %> + <% end %> + + <%= if @activity.local do %> + + + <% else %> + + <% end %> + + <%= for tag <- @data["tag"] || [] do %> + + <% end %> + + <%= for attachment <- @data["attachment"] || [] do %> + + <% end %> + + <%= if @data["inReplyTo"] do %> + + <% end %> + + <%= for id <- @activity.recipients do %> + <%= if id == Pleroma.Constants.as_public() do %> + + <% else %> + <%= unless Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) do %> + + <% end %> + <% end %> + <% end %> + + <%= for {emoji, file} <- @data["emoji"] || %{} do %> + + <% end %> + diff --git a/lib/pleroma/web/templates/feed/feed/_author.xml.eex b/lib/pleroma/web/templates/feed/feed/_author.xml.eex new file mode 100644 index 000000000..25cbffada --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/_author.xml.eex @@ -0,0 +1,17 @@ + + <%= @user.ap_id %> + http://activitystrea.ms/schema/1.0/person + <%= @user.ap_id %> + <%= @user.nickname %> + <%= @user.name %> + <%= escape(@user.bio) %> + <%= escape(@user.bio) %> + <%= @user.nickname %> + + <%= if User.banner_url(@user) do %> + + <% end %> + <%= if @user.local do %> + true + <% end %> + diff --git a/lib/pleroma/web/templates/feed/feed/feed.xml.eex b/lib/pleroma/web/templates/feed/feed/feed.xml.eex new file mode 100644 index 000000000..fbfdc46b5 --- /dev/null +++ b/lib/pleroma/web/templates/feed/feed/feed.xml.eex @@ -0,0 +1,26 @@ + + + + <%= feed_url(@conn, :feed, @user.nickname) <> ".atom" %> + <%= @user.nickname <> "'s timeline" %> + <%= most_recent_update(@activities, @user) %> + <%= logo(@user) %> + + + + + <%= render @view_module, "_author.xml", assigns %> + + <%= if last_activity(@activities) do %> + + <% end %> + + <%= for activity <- @activities do %> + <%= render @view_module, "_activity.xml", Map.merge(assigns, %{activity: activity, data: activity_object_data(activity)}) %> + <% end %> + diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index f05a84c7f..2305bb413 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -13,11 +13,34 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do alias Pleroma.Healthcheck alias Pleroma.Notification alias Pleroma.Plugs.AuthenticationPlug + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web alias Pleroma.Web.CommonAPI alias Pleroma.Web.WebFinger + plug( + OAuthScopesPlug, + %{scopes: ["follow", "write:follows"]} + when action in [:do_remote_follow, :follow_import] + ) + + plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks_import) + + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} + when action in [ + :change_email, + :change_password, + :delete_account, + :update_notificaton_settings, + :disable_account + ] + ) + + plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read) + plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version]) def help_test(conn, _params) do diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 5024ac70d..bf5a6ae42 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -6,12 +6,17 @@ defmodule Pleroma.Web.TwitterAPI.Controller do use Pleroma.Web, :controller alias Pleroma.Notification + alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.OAuth.Token alias Pleroma.Web.TwitterAPI.TokenView require Logger + plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read) + + plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) + action_fallback(:errors) def confirm_email(conn, %{"user_id" => uid, "token" => token}) do diff --git a/priv/repo/migrations/20170320193800_create_user.exs b/priv/repo/migrations/20170320193800_create_user.exs index 089964a26..e5f6ac52e 100644 --- a/priv/repo/migrations/20170320193800_create_user.exs +++ b/priv/repo/migrations/20170320193800_create_user.exs @@ -3,14 +3,13 @@ defmodule Pleroma.Repo.Migrations.CreatePleroma.User do def change do create_if_not_exists table(:users) do - add :email, :string - add :password_hash, :string - add :name, :string - add :nickname, :string - add :bio, :string + add(:email, :string) + add(:password_hash, :string) + add(:name, :string) + add(:nickname, :string) + add(:bio, :string) timestamps() end - end end diff --git a/priv/repo/migrations/20170321074828_create_activity.exs b/priv/repo/migrations/20170321074828_create_activity.exs index f5c872721..ab00a47c3 100644 --- a/priv/repo/migrations/20170321074828_create_activity.exs +++ b/priv/repo/migrations/20170321074828_create_activity.exs @@ -3,12 +3,11 @@ defmodule Pleroma.Repo.Migrations.CreatePleroma.Activity do def change do create_if_not_exists table(:activities) do - add :data, :map + add(:data, :map) timestamps() end - create_if_not_exists index(:activities, [:data], using: :gin) - + create_if_not_exists(index(:activities, [:data], using: :gin)) end end diff --git a/priv/repo/migrations/20170321074832_create_object.exs b/priv/repo/migrations/20170321074832_create_object.exs index b184672ad..c5a59ecba 100644 --- a/priv/repo/migrations/20170321074832_create_object.exs +++ b/priv/repo/migrations/20170321074832_create_object.exs @@ -3,10 +3,9 @@ defmodule Pleroma.Repo.Migrations.CreatePleroma.Object do def change do create_if_not_exists table(:objects) do - add :data, :map + add(:data, :map) timestamps() end - end end diff --git a/priv/repo/migrations/20170321133335_add_following_list_to_users.exs b/priv/repo/migrations/20170321133335_add_following_list_to_users.exs index b02c1272c..8dd83c3f8 100644 --- a/priv/repo/migrations/20170321133335_add_following_list_to_users.exs +++ b/priv/repo/migrations/20170321133335_add_following_list_to_users.exs @@ -3,7 +3,7 @@ defmodule Pleroma.Repo.Migrations.AddFollowingListToUsers do def change do alter table(:users) do - add :following, :map + add(:following, :map) end end end diff --git a/priv/repo/migrations/20170321143152_add_ap_id_to_users.exs b/priv/repo/migrations/20170321143152_add_ap_id_to_users.exs index e6f0366b5..38ceb87fd 100644 --- a/priv/repo/migrations/20170321143152_add_ap_id_to_users.exs +++ b/priv/repo/migrations/20170321143152_add_ap_id_to_users.exs @@ -3,7 +3,7 @@ defmodule Pleroma.Repo.Migrations.AddApIdToUsers do def change do alter table(:users) do - add :ap_id, :string + add(:ap_id, :string) end end end diff --git a/priv/repo/migrations/20170330153447_add_index_to_objects.exs b/priv/repo/migrations/20170330153447_add_index_to_objects.exs index 25e308533..93198b462 100644 --- a/priv/repo/migrations/20170330153447_add_index_to_objects.exs +++ b/priv/repo/migrations/20170330153447_add_index_to_objects.exs @@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddIndexToObjects do use Ecto.Migration def change do - create_if_not_exists index(:objects, [:data], using: :gin) + create_if_not_exists(index(:objects, [:data], using: :gin)) end end diff --git a/priv/repo/migrations/20170415141210_add_unique_index_to_email_and_nickname.exs b/priv/repo/migrations/20170415141210_add_unique_index_to_email_and_nickname.exs index 42da88954..b18c67dcb 100644 --- a/priv/repo/migrations/20170415141210_add_unique_index_to_email_and_nickname.exs +++ b/priv/repo/migrations/20170415141210_add_unique_index_to_email_and_nickname.exs @@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.AddUniqueIndexToEmailAndNickname do use Ecto.Migration def change do - create_if_not_exists unique_index(:users, [:email]) - create_if_not_exists unique_index(:users, [:nickname]) + create_if_not_exists(unique_index(:users, [:email])) + create_if_not_exists(unique_index(:users, [:nickname])) end end diff --git a/priv/repo/migrations/20170416122418_add_avatar_object_to_users.exs b/priv/repo/migrations/20170416122418_add_avatar_object_to_users.exs index b6d8742dc..e88752c30 100644 --- a/priv/repo/migrations/20170416122418_add_avatar_object_to_users.exs +++ b/priv/repo/migrations/20170416122418_add_avatar_object_to_users.exs @@ -3,7 +3,7 @@ defmodule Pleroma.Repo.Migrations.AddAvatarObjectToUsers do def change do alter table(:users) do - add :avatar, :map + add(:avatar, :map) end end end diff --git a/priv/repo/migrations/20170418200143_create_webssub_server_subscription.exs b/priv/repo/migrations/20170418200143_create_webssub_server_subscription.exs index 243280378..3d94e4ee7 100644 --- a/priv/repo/migrations/20170418200143_create_webssub_server_subscription.exs +++ b/priv/repo/migrations/20170418200143_create_webssub_server_subscription.exs @@ -3,11 +3,11 @@ defmodule Pleroma.Repo.Migrations.CreateWebsubServerSubscription do def change do create_if_not_exists table(:websub_server_subscriptions) do - add :topic, :string - add :callback, :string - add :secret, :string - add :valid_until, :naive_datetime - add :state, :string + add(:topic, :string) + add(:callback, :string) + add(:secret, :string) + add(:valid_until, :naive_datetime) + add(:state, :string) timestamps() end diff --git a/priv/repo/migrations/20170423154511_add_fields_to_users.exs b/priv/repo/migrations/20170423154511_add_fields_to_users.exs index 84de74bc4..a079c73bd 100644 --- a/priv/repo/migrations/20170423154511_add_fields_to_users.exs +++ b/priv/repo/migrations/20170423154511_add_fields_to_users.exs @@ -3,8 +3,8 @@ defmodule Pleroma.Repo.Migrations.AddFieldsToUsers do def change do alter table(:users) do - add :local, :boolean, default: true - add :info, :map + add(:local, :boolean, default: true) + add(:info, :map) end end end diff --git a/priv/repo/migrations/20170426154155_create_websub_client_subscription.exs b/priv/repo/migrations/20170426154155_create_websub_client_subscription.exs index 4b79d7506..d020614e1 100644 --- a/priv/repo/migrations/20170426154155_create_websub_client_subscription.exs +++ b/priv/repo/migrations/20170426154155_create_websub_client_subscription.exs @@ -3,11 +3,11 @@ defmodule Pleroma.Repo.Migrations.CreateWebsubClientSubscription do def change do create_if_not_exists table(:websub_client_subscriptions) do - add :topic, :string - add :secret, :string - add :valid_until, :naive_datetime_usec - add :state, :string - add :subscribers, :map + add(:topic, :string) + add(:secret, :string) + add(:valid_until, :naive_datetime_usec) + add(:state, :string) + add(:subscribers, :map) timestamps() end diff --git a/priv/repo/migrations/20170427054757_add_user_and_hub.exs b/priv/repo/migrations/20170427054757_add_user_and_hub.exs index 4f9a520bd..f33a8572f 100644 --- a/priv/repo/migrations/20170427054757_add_user_and_hub.exs +++ b/priv/repo/migrations/20170427054757_add_user_and_hub.exs @@ -3,8 +3,8 @@ defmodule Pleroma.Repo.Migrations.AddUserAndHub do def change do alter table(:websub_client_subscriptions) do - add :hub, :string - add :user_id, references(:users) + add(:hub, :string) + add(:user_id, references(:users)) end end end diff --git a/priv/repo/migrations/20170501133231_add_id_contraints_to_activities_and_objects_part_two.exs b/priv/repo/migrations/20170501133231_add_id_contraints_to_activities_and_objects_part_two.exs index f5e5cd269..ecc7c23cc 100644 --- a/priv/repo/migrations/20170501133231_add_id_contraints_to_activities_and_objects_part_two.exs +++ b/priv/repo/migrations/20170501133231_add_id_contraints_to_activities_and_objects_part_two.exs @@ -2,10 +2,16 @@ defmodule Pleroma.Repo.Migrations.AddIdContraintsToActivitiesAndObjectsPartTwo d use Ecto.Migration def up do - drop_if_exists index(:objects, ["(data->>\"id\")"], name: :objects_unique_apid_index) - drop_if_exists index(:activities, ["(data->>\"id\")"], name: :activities_unique_apid_index) - create_if_not_exists unique_index(:objects, ["(data->>'id')"], name: :objects_unique_apid_index) - create_if_not_exists unique_index(:activities, ["(data->>'id')"], name: :activities_unique_apid_index) + drop_if_exists(index(:objects, ["(data->>\"id\")"], name: :objects_unique_apid_index)) + drop_if_exists(index(:activities, ["(data->>\"id\")"], name: :activities_unique_apid_index)) + + create_if_not_exists( + unique_index(:objects, ["(data->>'id')"], name: :objects_unique_apid_index) + ) + + create_if_not_exists( + unique_index(:activities, ["(data->>'id')"], name: :activities_unique_apid_index) + ) end def down, do: :ok diff --git a/priv/repo/migrations/20170502083023_add_local_field_to_activities.exs b/priv/repo/migrations/20170502083023_add_local_field_to_activities.exs index cebc11d21..6b61bd464 100644 --- a/priv/repo/migrations/20170502083023_add_local_field_to_activities.exs +++ b/priv/repo/migrations/20170502083023_add_local_field_to_activities.exs @@ -3,9 +3,9 @@ defmodule Pleroma.Repo.Migrations.AddLocalFieldToActivities do def change do alter table(:activities) do - add :local, :boolean, default: true + add(:local, :boolean, default: true) end - create_if_not_exists index(:activities, [:local]) + create_if_not_exists(index(:activities, [:local])) end end diff --git a/priv/repo/migrations/20170506222027_add_unique_index_to_apid.exs b/priv/repo/migrations/20170506222027_add_unique_index_to_apid.exs index 1b7e33b70..80f50029a 100644 --- a/priv/repo/migrations/20170506222027_add_unique_index_to_apid.exs +++ b/priv/repo/migrations/20170506222027_add_unique_index_to_apid.exs @@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddUniqueIndexToAPID do use Ecto.Migration def change do - create_if_not_exists unique_index(:users, [:ap_id]) + create_if_not_exists(unique_index(:users, [:ap_id])) end end diff --git a/priv/repo/migrations/20170529093232_longer_bios.exs b/priv/repo/migrations/20170529093232_longer_bios.exs index 9188f4bee..e25e5e144 100644 --- a/priv/repo/migrations/20170529093232_longer_bios.exs +++ b/priv/repo/migrations/20170529093232_longer_bios.exs @@ -3,14 +3,13 @@ defmodule Pleroma.Repo.Migrations.LongerBios do def up do alter table(:users) do - modify :bio, :text + modify(:bio, :text) end end def down do alter table(:users) do - modify :bio, :string + modify(:bio, :string) end end - end diff --git a/priv/repo/migrations/20170620095947_remove_activities_index.exs b/priv/repo/migrations/20170620095947_remove_activities_index.exs index e7d41eac4..ea3d4a509 100644 --- a/priv/repo/migrations/20170620095947_remove_activities_index.exs +++ b/priv/repo/migrations/20170620095947_remove_activities_index.exs @@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.RemoveActivitiesIndex do use Ecto.Migration def change do - drop_if_exists index(:activities, [:data]) + drop_if_exists(index(:activities, [:data])) end end diff --git a/priv/repo/migrations/20170620142420_add_object_activity_index_part_two.exs b/priv/repo/migrations/20170620142420_add_object_activity_index_part_two.exs index c95218fad..c015afbe1 100644 --- a/priv/repo/migrations/20170620142420_add_object_activity_index_part_two.exs +++ b/priv/repo/migrations/20170620142420_add_object_activity_index_part_two.exs @@ -2,7 +2,16 @@ defmodule Pleroma.Repo.Migrations.AddObjectActivityIndexPartTwo do use Ecto.Migration def change do - drop_if_exists index(:objects, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index) - create_if_not_exists index(:activities, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index) + drop_if_exists( + index(:objects, ["(data->'object'->>'id')", "(data->>'type')"], + name: :activities_create_objects_index + ) + ) + + create_if_not_exists( + index(:activities, ["(data->'object'->>'id')", "(data->>'type')"], + name: :activities_create_objects_index + ) + ) end end diff --git a/priv/repo/migrations/20170701142005_add_actor_index_to_activity.exs b/priv/repo/migrations/20170701142005_add_actor_index_to_activity.exs index 807fe3728..220c48abd 100644 --- a/priv/repo/migrations/20170701142005_add_actor_index_to_activity.exs +++ b/priv/repo/migrations/20170701142005_add_actor_index_to_activity.exs @@ -2,6 +2,8 @@ defmodule Pleroma.Repo.Migrations.AddActorIndexToActivity do use Ecto.Migration def change do - create_if_not_exists index(:activities, ["(data->>'actor')", "inserted_at desc"], name: :activities_actor_index) + create_if_not_exists( + index(:activities, ["(data->>'actor')", "inserted_at desc"], name: :activities_actor_index) + ) end end diff --git a/priv/repo/migrations/20170719152213_add_follower_address_to_user.exs b/priv/repo/migrations/20170719152213_add_follower_address_to_user.exs index 591164be5..be5eca36c 100644 --- a/priv/repo/migrations/20170719152213_add_follower_address_to_user.exs +++ b/priv/repo/migrations/20170719152213_add_follower_address_to_user.exs @@ -3,13 +3,13 @@ defmodule Pleroma.Repo.Migrations.AddFollowerAddressToUser do def up do alter table(:users) do - add :follower_address, :string, unique: true + add(:follower_address, :string, unique: true) end end def down do alter table(:users) do - remove :follower_address + remove(:follower_address) end end end diff --git a/priv/repo/migrations/20170906120646_add_mastodon_apps.exs b/priv/repo/migrations/20170906120646_add_mastodon_apps.exs index ccd5e3fe2..0e01625ff 100644 --- a/priv/repo/migrations/20170906120646_add_mastodon_apps.exs +++ b/priv/repo/migrations/20170906120646_add_mastodon_apps.exs @@ -3,12 +3,12 @@ defmodule Pleroma.Repo.Migrations.AddMastodonApps do def change do create_if_not_exists table(:apps) do - add :client_name, :string - add :redirect_uris, :string - add :scopes, :string - add :website, :string - add :client_id, :string - add :client_secret, :string + add(:client_name, :string) + add(:redirect_uris, :string) + add(:scopes, :string) + add(:website, :string) + add(:client_id, :string) + add(:client_secret, :string) timestamps() end diff --git a/priv/repo/migrations/20170906143140_create_o_auth_authorizations.exs b/priv/repo/migrations/20170906143140_create_o_auth_authorizations.exs index 63b25c537..9af8315a8 100644 --- a/priv/repo/migrations/20170906143140_create_o_auth_authorizations.exs +++ b/priv/repo/migrations/20170906143140_create_o_auth_authorizations.exs @@ -3,11 +3,11 @@ defmodule Pleroma.Repo.Migrations.CreateOAuthAuthorizations do def change do create_if_not_exists table(:oauth_authorizations) do - add :app_id, references(:apps) - add :user_id, references(:users) - add :token, :string - add :valid_until, :naive_datetime_usec - add :used, :boolean, default: false + add(:app_id, references(:apps)) + add(:user_id, references(:users)) + add(:token, :string) + add(:valid_until, :naive_datetime_usec) + add(:used, :boolean, default: false) timestamps() end diff --git a/priv/repo/migrations/20170906152508_create_o_auth_token.exs b/priv/repo/migrations/20170906152508_create_o_auth_token.exs index 08471bbf8..bfad98b76 100644 --- a/priv/repo/migrations/20170906152508_create_o_auth_token.exs +++ b/priv/repo/migrations/20170906152508_create_o_auth_token.exs @@ -3,11 +3,11 @@ defmodule Pleroma.Repo.Migrations.CreateOAuthToken do def change do create_if_not_exists table(:oauth_tokens) do - add :app_id, references(:apps) - add :user_id, references(:users) - add :token, :string - add :refresh_token, :string - add :valid_until, :naive_datetime_usec + add(:app_id, references(:apps)) + add(:user_id, references(:users)) + add(:token, :string) + add(:refresh_token, :string) + add(:valid_until, :naive_datetime_usec) timestamps() end diff --git a/priv/repo/migrations/20170911123607_create_notifications.exs b/priv/repo/migrations/20170911123607_create_notifications.exs index 50de9c5f1..36facc5a0 100644 --- a/priv/repo/migrations/20170911123607_create_notifications.exs +++ b/priv/repo/migrations/20170911123607_create_notifications.exs @@ -3,13 +3,13 @@ defmodule Pleroma.Repo.Migrations.CreateNotifications do def change do create_if_not_exists table(:notifications) do - add :user_id, references(:users, on_delete: :delete_all) - add :activity_id, references(:activities, on_delete: :delete_all) - add :seen, :boolean, default: false + add(:user_id, references(:users, on_delete: :delete_all)) + add(:activity_id, references(:activities, on_delete: :delete_all)) + add(:seen, :boolean, default: false) timestamps() end - create_if_not_exists index(:notifications, [:user_id]) + create_if_not_exists(index(:notifications, [:user_id])) end end diff --git a/priv/repo/migrations/20170912114248_add_context_index.exs b/priv/repo/migrations/20170912114248_add_context_index.exs index 83c585136..400a432ff 100644 --- a/priv/repo/migrations/20170912114248_add_context_index.exs +++ b/priv/repo/migrations/20170912114248_add_context_index.exs @@ -3,6 +3,11 @@ defmodule Pleroma.Repo.Migrations.AddContextIndex do @disable_ddl_transaction true def change do - create index(:activities, ["(data->>'type')", "(data->>'context')"], name: :activities_context_index, concurrently: true) + create( + index(:activities, ["(data->>'type')", "(data->>'context')"], + name: :activities_context_index, + concurrently: true + ) + ) end end diff --git a/priv/repo/migrations/20170916090107_add_fts_index_to_activities.exs b/priv/repo/migrations/20170916090107_add_fts_index_to_activities.exs index c17da8309..717e25412 100644 --- a/priv/repo/migrations/20170916090107_add_fts_index_to_activities.exs +++ b/priv/repo/migrations/20170916090107_add_fts_index_to_activities.exs @@ -3,6 +3,12 @@ defmodule Pleroma.Repo.Migrations.AddFTSIndexToActivities do @disable_ddl_transaction true def change do - create index(:activities, ["(to_tsvector('english', data->'object'->>'content'))"], concurrently: true, using: :gin, name: :activities_fts) + create( + index(:activities, ["(to_tsvector('english', data->'object'->>'content'))"], + concurrently: true, + using: :gin, + name: :activities_fts + ) + ) end end diff --git a/priv/repo/migrations/20170917120416_add_tag_index.exs b/priv/repo/migrations/20170917120416_add_tag_index.exs index d9391dda9..c69e0ef8f 100644 --- a/priv/repo/migrations/20170917120416_add_tag_index.exs +++ b/priv/repo/migrations/20170917120416_add_tag_index.exs @@ -4,6 +4,12 @@ defmodule Pleroma.Repo.Migrations.AddTagIndex do @disable_ddl_transaction true def change do - create index(:activities, ["(data #> '{\"object\",\"tag\"}')"], concurrently: true, using: :gin, name: :activities_tags) + create( + index(:activities, ["(data #> '{\"object\",\"tag\"}')"], + concurrently: true, + using: :gin, + name: :activities_tags + ) + ) end end diff --git a/priv/repo/migrations/20171019141706_create_password_reset_tokens.exs b/priv/repo/migrations/20171019141706_create_password_reset_tokens.exs index dde0f945f..2be50d15e 100644 --- a/priv/repo/migrations/20171019141706_create_password_reset_tokens.exs +++ b/priv/repo/migrations/20171019141706_create_password_reset_tokens.exs @@ -3,9 +3,9 @@ defmodule Pleroma.Repo.Migrations.CreatePasswordResetTokens do def change do create_if_not_exists table(:password_reset_tokens) do - add :token, :string - add :user_id, references(:users) - add :used, :boolean, default: false + add(:token, :string) + add(:user_id, references(:users)) + add(:used, :boolean, default: false) timestamps() end diff --git a/priv/repo/migrations/20171023155035_add_second_object_index_to_activty.exs b/priv/repo/migrations/20171023155035_add_second_object_index_to_activty.exs index c6df53ec9..261940a96 100644 --- a/priv/repo/migrations/20171023155035_add_second_object_index_to_activty.exs +++ b/priv/repo/migrations/20171023155035_add_second_object_index_to_activty.exs @@ -4,7 +4,17 @@ defmodule Pleroma.Repo.Migrations.AddSecondObjectIndexToActivty do @disable_ddl_transaction true def change do - drop_if_exists index(:activities, ["(data->'object'->>'id')", "(data->>'type')"], name: :activities_create_objects_index) - create index(:activities, ["(coalesce(data->'object'->>'id', data->>'object'))"], name: :activities_create_objects_index, concurrently: true) + drop_if_exists( + index(:activities, ["(data->'object'->>'id')", "(data->>'type')"], + name: :activities_create_objects_index + ) + ) + + create( + index(:activities, ["(coalesce(data->'object'->>'id', data->>'object'))"], + name: :activities_create_objects_index, + concurrently: true + ) + ) end end diff --git a/priv/repo/migrations/20171024090137_drop_object_index.exs b/priv/repo/migrations/20171024090137_drop_object_index.exs index 29b4c9333..d417577ae 100644 --- a/priv/repo/migrations/20171024090137_drop_object_index.exs +++ b/priv/repo/migrations/20171024090137_drop_object_index.exs @@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.DropObjectIndex do use Ecto.Migration def change do - drop_if_exists index(:objects, [:data], using: :gin) + drop_if_exists(index(:objects, [:data], using: :gin)) end end diff --git a/priv/repo/migrations/20171024121413_add_object_actor_index.exs b/priv/repo/migrations/20171024121413_add_object_actor_index.exs index 344c9c825..78084536c 100644 --- a/priv/repo/migrations/20171024121413_add_object_actor_index.exs +++ b/priv/repo/migrations/20171024121413_add_object_actor_index.exs @@ -4,6 +4,11 @@ defmodule Pleroma.Repo.Migrations.AddObjectActorIndex do @disable_ddl_transaction true def change do - create index(:objects, ["(data->>'actor')", "(data->>'type')"], concurrently: true, name: :objects_actor_type) + create( + index(:objects, ["(data->>'actor')", "(data->>'type')"], + concurrently: true, + name: :objects_actor_type + ) + ) end end diff --git a/priv/repo/migrations/20171109091239_add_actor_to_activity.exs b/priv/repo/migrations/20171109091239_add_actor_to_activity.exs index fb5f80c98..91348f5c3 100644 --- a/priv/repo/migrations/20171109091239_add_actor_to_activity.exs +++ b/priv/repo/migrations/20171109091239_add_actor_to_activity.exs @@ -5,16 +5,17 @@ defmodule Pleroma.Repo.Migrations.AddActorToActivity do def up do alter table(:activities) do - add :actor, :string + add(:actor, :string) end - create index(:activities, [:actor, "id DESC NULLS LAST"], concurrently: true) + create(index(:activities, [:actor, "id DESC NULLS LAST"], concurrently: true)) end def down do - drop_if_exists index(:activities, [:actor, "id DESC NULLS LAST"]) + drop_if_exists(index(:activities, [:actor, "id DESC NULLS LAST"])) + alter table(:activities) do - remove :actor + remove(:actor) end end end diff --git a/priv/repo/migrations/20171109114020_fill_actor_field.exs b/priv/repo/migrations/20171109114020_fill_actor_field.exs index 255ca46d5..fb7eca692 100644 --- a/priv/repo/migrations/20171109114020_fill_actor_field.exs +++ b/priv/repo/migrations/20171109114020_fill_actor_field.exs @@ -5,17 +5,19 @@ defmodule Pleroma.Repo.Migrations.FillActorField do def up do max = Repo.aggregate(Activity, :max, :id) + if max do IO.puts("#{max} activities") - chunks = 0..(round(max / 10_000)) + chunks = 0..round(max / 10_000) - Enum.each(chunks, fn (i) -> + Enum.each(chunks, fn i -> min = i * 10_000 max = min + 10_000 + execute(""" update activities set actor = data->>'actor' where id > #{min} and id <= #{max}; """) - |> IO.inspect + |> IO.inspect() end) end end @@ -23,4 +25,3 @@ def up do def down do end end - diff --git a/priv/repo/migrations/20171109141309_add_sort_index_to_activities.exs b/priv/repo/migrations/20171109141309_add_sort_index_to_activities.exs index 2d21c56ca..37fb2ce32 100644 --- a/priv/repo/migrations/20171109141309_add_sort_index_to_activities.exs +++ b/priv/repo/migrations/20171109141309_add_sort_index_to_activities.exs @@ -3,6 +3,6 @@ defmodule Pleroma.Repo.Migrations.AddSortIndexToActivities do @disable_ddl_transaction true def change do - create index(:activities, ["id desc nulls last"], concurrently: true) + create(index(:activities, ["id desc nulls last"], concurrently: true)) end end diff --git a/priv/repo/migrations/20171130135819_add_local_index_to_user.exs b/priv/repo/migrations/20171130135819_add_local_index_to_user.exs index 3438bbbc4..76bf9584e 100644 --- a/priv/repo/migrations/20171130135819_add_local_index_to_user.exs +++ b/priv/repo/migrations/20171130135819_add_local_index_to_user.exs @@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddLocalIndexToUser do use Ecto.Migration def change do - create_if_not_exists index(:users, [:local]) + create_if_not_exists(index(:users, [:local])) end end diff --git a/priv/repo/migrations/20171212163643_add_recipients_to_activities.exs b/priv/repo/migrations/20171212163643_add_recipients_to_activities.exs index 4520b398e..6344fbeee 100644 --- a/priv/repo/migrations/20171212163643_add_recipients_to_activities.exs +++ b/priv/repo/migrations/20171212163643_add_recipients_to_activities.exs @@ -3,9 +3,9 @@ defmodule Pleroma.Repo.Migrations.AddRecipientsToActivities do def change do alter table(:activities) do - add :recipients, {:array, :string} + add(:recipients, {:array, :string}) end - create_if_not_exists index(:activities, [:recipients], using: :gin) + create_if_not_exists(index(:activities, [:recipients], using: :gin)) end end diff --git a/priv/repo/migrations/20171212164525_fill_recipients_in_activities.exs b/priv/repo/migrations/20171212164525_fill_recipients_in_activities.exs index 87de64ca5..6dfa93716 100644 --- a/priv/repo/migrations/20171212164525_fill_recipients_in_activities.exs +++ b/priv/repo/migrations/20171212164525_fill_recipients_in_activities.exs @@ -4,17 +4,21 @@ defmodule Pleroma.Repo.Migrations.FillRecipientsInActivities do def up do max = Repo.aggregate(Activity, :max, :id) + if max do IO.puts("#{max} activities") - chunks = 0..(round(max / 10_000)) + chunks = 0..round(max / 10_000) - Enum.each(chunks, fn (i) -> + Enum.each(chunks, fn i -> min = i * 10_000 max = min + 10_000 + execute(""" - update activities set recipients = array(select jsonb_array_elements_text(data->'to')) where id > #{min} and id <= #{max}; + update activities set recipients = array(select jsonb_array_elements_text(data->'to')) where id > #{ + min + } and id <= #{max}; """) - |> IO.inspect + |> IO.inspect() end) end end diff --git a/priv/repo/migrations/20180221210540_make_following_postgres_array.exs b/priv/repo/migrations/20180221210540_make_following_postgres_array.exs index 5a8f8f669..34e94fdc8 100644 --- a/priv/repo/migrations/20180221210540_make_following_postgres_array.exs +++ b/priv/repo/migrations/20180221210540_make_following_postgres_array.exs @@ -3,17 +3,18 @@ defmodule Pleroma.Repo.Migrations.MakeFollowingPostgresArray do def up do alter table(:users) do - add :following_temp, {:array, :string} + add(:following_temp, {:array, :string}) end - execute """ + execute(""" update users set following_temp = array(select jsonb_array_elements_text(following)); - """ + """) alter table(:users) do - remove :following + remove(:following) end - rename table(:users), :following_temp, to: :following + + rename(table(:users), :following_temp, to: :following) end def down, do: :ok diff --git a/priv/repo/migrations/20180325172351_add_follower_address_index_to_users.exs b/priv/repo/migrations/20180325172351_add_follower_address_index_to_users.exs index 234d33735..18b54411c 100644 --- a/priv/repo/migrations/20180325172351_add_follower_address_index_to_users.exs +++ b/priv/repo/migrations/20180325172351_add_follower_address_index_to_users.exs @@ -3,7 +3,7 @@ defmodule Pleroma.Repo.Migrations.AddFollowerAddressIndexToUsers do @disable_ddl_transaction true def change do - create index(:users, [:follower_address], concurrently: true) - create index(:users, [:following], concurrently: true, using: :gin) + create(index(:users, [:follower_address], concurrently: true)) + create(index(:users, [:following], concurrently: true, using: :gin)) end end diff --git a/priv/repo/migrations/20180327174350_drop_local_index_on_activities.exs b/priv/repo/migrations/20180327174350_drop_local_index_on_activities.exs index 35c4ce62f..1574e0e00 100644 --- a/priv/repo/migrations/20180327174350_drop_local_index_on_activities.exs +++ b/priv/repo/migrations/20180327174350_drop_local_index_on_activities.exs @@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.DropLocalIndexOnActivities do use Ecto.Migration def change do - drop_if_exists index(:users, [:local]) + drop_if_exists(index(:users, [:local])) end end diff --git a/priv/repo/migrations/20180327175831_actually_drop_local_index.exs b/priv/repo/migrations/20180327175831_actually_drop_local_index.exs index 7556336ed..3d52c7c80 100644 --- a/priv/repo/migrations/20180327175831_actually_drop_local_index.exs +++ b/priv/repo/migrations/20180327175831_actually_drop_local_index.exs @@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.ActuallyDropLocalIndex do use Ecto.Migration def change do - create_if_not_exists index(:users, [:local]) - drop_if_exists index("activities", :local) + create_if_not_exists(index(:users, [:local])) + drop_if_exists(index("activities", :local)) end end diff --git a/priv/repo/migrations/20180429094642_create_lists.exs b/priv/repo/migrations/20180429094642_create_lists.exs index 9d3ce50b3..e1eb7e426 100644 --- a/priv/repo/migrations/20180429094642_create_lists.exs +++ b/priv/repo/migrations/20180429094642_create_lists.exs @@ -3,13 +3,13 @@ defmodule Pleroma.Repo.Migrations.CreateLists do def change do create_if_not_exists table(:lists) do - add :user_id, references(:users, on_delete: :delete_all) - add :title, :string - add :following, {:array, :string} + add(:user_id, references(:users, on_delete: :delete_all)) + add(:title, :string) + add(:following, {:array, :string}) timestamps() end - create_if_not_exists index(:lists, [:user_id]) + create_if_not_exists(index(:lists, [:user_id])) end end diff --git a/priv/repo/migrations/20180513104714_modify_activity_index.exs b/priv/repo/migrations/20180513104714_modify_activity_index.exs index 2df530839..ec0efa238 100644 --- a/priv/repo/migrations/20180513104714_modify_activity_index.exs +++ b/priv/repo/migrations/20180513104714_modify_activity_index.exs @@ -3,7 +3,7 @@ defmodule Pleroma.Repo.Migrations.ModifyActivityIndex do @disable_ddl_transaction true def change do - create index(:activities, ["id desc nulls last", "local"], concurrently: true) - drop_if_exists index(:activities, ["id desc nulls last"]) + create(index(:activities, ["id desc nulls last", "local"], concurrently: true)) + drop_if_exists(index(:activities, ["id desc nulls last"])) end end diff --git a/priv/repo/migrations/20180516144508_add_trigram_extension.exs b/priv/repo/migrations/20180516144508_add_trigram_extension.exs index f2f0fca86..ff0710f84 100644 --- a/priv/repo/migrations/20180516144508_add_trigram_extension.exs +++ b/priv/repo/migrations/20180516144508_add_trigram_extension.exs @@ -4,8 +4,15 @@ defmodule Pleroma.Repo.Migrations.AddTrigramExtension do def up do Logger.warn("ATTENTION ATTENTION ATTENTION\n") - Logger.warn("This will try to create the pg_trgm extension on your database. If your database user does NOT have the necessary rights, you will have to do it manually and re-run the migrations.\nYou can probably do this by running the following:\n") - Logger.warn("sudo -u postgres psql pleroma_dev -c \"create extension if not exists pg_trgm\"\n") + + Logger.warn( + "This will try to create the pg_trgm extension on your database. If your database user does NOT have the necessary rights, you will have to do it manually and re-run the migrations.\nYou can probably do this by running the following:\n" + ) + + Logger.warn( + "sudo -u postgres psql pleroma_dev -c \"create extension if not exists pg_trgm\"\n" + ) + execute("create extension if not exists pg_trgm") end diff --git a/priv/repo/migrations/20180516154905_create_user_trigram_index.exs b/priv/repo/migrations/20180516154905_create_user_trigram_index.exs index 58622a87e..0713a7297 100644 --- a/priv/repo/migrations/20180516154905_create_user_trigram_index.exs +++ b/priv/repo/migrations/20180516154905_create_user_trigram_index.exs @@ -2,6 +2,8 @@ defmodule Pleroma.Repo.Migrations.CreateUserTrigramIndex do use Ecto.Migration def change do - create_if_not_exists index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist) + create_if_not_exists( + index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist) + ) end end diff --git a/priv/repo/migrations/20180530123448_add_list_follow_index.exs b/priv/repo/migrations/20180530123448_add_list_follow_index.exs index 86b8de30a..57f8d478f 100644 --- a/priv/repo/migrations/20180530123448_add_list_follow_index.exs +++ b/priv/repo/migrations/20180530123448_add_list_follow_index.exs @@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddListFollowIndex do use Ecto.Migration def change do - create_if_not_exists index(:lists, [:following]) + create_if_not_exists(index(:lists, [:following])) end end diff --git a/priv/repo/migrations/20180606173637_create_apid_host_extraction_index.exs b/priv/repo/migrations/20180606173637_create_apid_host_extraction_index.exs index 9831a1b82..07b3f2875 100644 --- a/priv/repo/migrations/20180606173637_create_apid_host_extraction_index.exs +++ b/priv/repo/migrations/20180606173637_create_apid_host_extraction_index.exs @@ -3,6 +3,11 @@ defmodule Pleroma.Repo.Migrations.CreateApidHostExtractionIndex do @disable_ddl_transaction true def change do - create index(:activities, ["(split_part(actor, '/', 3))"], concurrently: true, name: :activities_hosts) + create( + index(:activities, ["(split_part(actor, '/', 3))"], + concurrently: true, + name: :activities_hosts + ) + ) end end diff --git a/priv/repo/migrations/20180612110515_create_user_invite_tokens.exs b/priv/repo/migrations/20180612110515_create_user_invite_tokens.exs index faee379f0..a75ff2a51 100644 --- a/priv/repo/migrations/20180612110515_create_user_invite_tokens.exs +++ b/priv/repo/migrations/20180612110515_create_user_invite_tokens.exs @@ -3,8 +3,8 @@ defmodule Pleroma.Repo.Migrations.CreateUserInviteTokens do def change do create_if_not_exists table(:user_invite_tokens) do - add :token, :string - add :used, :boolean, default: false + add(:token, :string) + add(:used, :boolean, default: false) timestamps() end diff --git a/priv/repo/migrations/20180617221540_create_activities_in_reply_to_index.exs b/priv/repo/migrations/20180617221540_create_activities_in_reply_to_index.exs index 1fee6fd7a..c8a0e60a0 100644 --- a/priv/repo/migrations/20180617221540_create_activities_in_reply_to_index.exs +++ b/priv/repo/migrations/20180617221540_create_activities_in_reply_to_index.exs @@ -3,6 +3,11 @@ defmodule Pleroma.Repo.Migrations.CreateActivitiesInReplyToIndex do @disable_ddl_transaction true def change do - create index(:activities, ["(data->'object'->>'inReplyTo')"], concurrently: true, name: :activities_in_reply_to) + create( + index(:activities, ["(data->'object'->>'inReplyTo')"], + concurrently: true, + name: :activities_in_reply_to + ) + ) end end diff --git a/priv/repo/migrations/20180813003722_create_filters.exs b/priv/repo/migrations/20180813003722_create_filters.exs index 541cf46a1..7803558df 100644 --- a/priv/repo/migrations/20180813003722_create_filters.exs +++ b/priv/repo/migrations/20180813003722_create_filters.exs @@ -3,18 +3,21 @@ defmodule Pleroma.Repo.Migrations.CreateFilters do def change do create_if_not_exists table(:filters) do - add :user_id, references(:users, on_delete: :delete_all) - add :filter_id, :integer - add :hide, :boolean - add :phrase, :string - add :context, {:array, :string} - add :expires_at, :utc_datetime - add :whole_word, :boolean + add(:user_id, references(:users, on_delete: :delete_all)) + add(:filter_id, :integer) + add(:hide, :boolean) + add(:phrase, :string) + add(:context, {:array, :string}) + add(:expires_at, :utc_datetime) + add(:whole_word, :boolean) timestamps() end - create_if_not_exists index(:filters, [:user_id]) - create_if_not_exists index(:filters, [:phrase], where: "hide = true", name: :hided_phrases_index) + create_if_not_exists(index(:filters, [:user_id])) + + create_if_not_exists( + index(:filters, [:phrase], where: "hide = true", name: :hided_phrases_index) + ) end end diff --git a/priv/repo/migrations/20180829082446_add_recipients_to_and_cc_fields_to_activities.exs b/priv/repo/migrations/20180829082446_add_recipients_to_and_cc_fields_to_activities.exs index af9d521c0..481986039 100644 --- a/priv/repo/migrations/20180829082446_add_recipients_to_and_cc_fields_to_activities.exs +++ b/priv/repo/migrations/20180829082446_add_recipients_to_and_cc_fields_to_activities.exs @@ -3,11 +3,11 @@ defmodule Pleroma.Repo.Migrations.AddRecipientsToAndCcFieldsToActivities do def change do alter table(:activities) do - add :recipients_to, {:array, :string} - add :recipients_cc, {:array, :string} + add(:recipients_to, {:array, :string}) + add(:recipients_cc, {:array, :string}) end - create_if_not_exists index(:activities, [:recipients_to], using: :gin) - create_if_not_exists index(:activities, [:recipients_cc], using: :gin) + create_if_not_exists(index(:activities, [:recipients_to], using: :gin)) + create_if_not_exists(index(:activities, [:recipients_cc], using: :gin)) end end diff --git a/priv/repo/migrations/20180829182612_activities_add_to_cc_indices.exs b/priv/repo/migrations/20180829182612_activities_add_to_cc_indices.exs index 9d31f6779..1f9f97861 100644 --- a/priv/repo/migrations/20180829182612_activities_add_to_cc_indices.exs +++ b/priv/repo/migrations/20180829182612_activities_add_to_cc_indices.exs @@ -2,7 +2,12 @@ defmodule Pleroma.Repo.Migrations.ActivitiesAddToCcIndices do use Ecto.Migration def change do - create_if_not_exists index(:activities, ["(data->'to')"], name: :activities_to_index, using: :gin) - create_if_not_exists index(:activities, ["(data->'cc')"], name: :activities_cc_index, using: :gin) + create_if_not_exists( + index(:activities, ["(data->'to')"], name: :activities_to_index, using: :gin) + ) + + create_if_not_exists( + index(:activities, ["(data->'cc')"], name: :activities_cc_index, using: :gin) + ) end end diff --git a/priv/repo/migrations/20180829183529_remove_recipients_to_and_cc_fields_from_activities.exs b/priv/repo/migrations/20180829183529_remove_recipients_to_and_cc_fields_from_activities.exs index 017ef161f..65576b8d5 100644 --- a/priv/repo/migrations/20180829183529_remove_recipients_to_and_cc_fields_from_activities.exs +++ b/priv/repo/migrations/20180829183529_remove_recipients_to_and_cc_fields_from_activities.exs @@ -3,15 +3,15 @@ defmodule Pleroma.Repo.Migrations.RemoveRecipientsToAndCcFieldsFromActivities do def up do alter table(:activities) do - remove :recipients_to - remove :recipients_cc + remove(:recipients_to) + remove(:recipients_cc) end end def down do alter table(:activities) do - add :recipients_to, {:array, :string} - add :recipients_cc, {:array, :string} + add(:recipients_to, {:array, :string}) + add(:recipients_cc, {:array, :string}) end end end diff --git a/priv/repo/migrations/20180903114437_users_add_is_moderator_index.exs b/priv/repo/migrations/20180903114437_users_add_is_moderator_index.exs index adce28bdf..cbe79de05 100644 --- a/priv/repo/migrations/20180903114437_users_add_is_moderator_index.exs +++ b/priv/repo/migrations/20180903114437_users_add_is_moderator_index.exs @@ -2,6 +2,8 @@ defmodule Pleroma.Repo.Migrations.UsersAddIsModeratorIndex do use Ecto.Migration def change do - create_if_not_exists index(:users, ["(info->'is_moderator')"], name: :users_is_moderator_index, using: :gin) + create_if_not_exists( + index(:users, ["(info->'is_moderator')"], name: :users_is_moderator_index, using: :gin) + ) end end diff --git a/priv/repo/migrations/20180918182427_create_push_subscriptions.exs b/priv/repo/migrations/20180918182427_create_push_subscriptions.exs index 36bdf322a..c1b55d018 100644 --- a/priv/repo/migrations/20180918182427_create_push_subscriptions.exs +++ b/priv/repo/migrations/20180918182427_create_push_subscriptions.exs @@ -3,16 +3,16 @@ defmodule Pleroma.Repo.Migrations.CreatePushSubscriptions do def change do create_if_not_exists table("push_subscriptions") do - add :user_id, references("users", on_delete: :delete_all) - add :token_id, references("oauth_tokens", on_delete: :delete_all) - add :endpoint, :string - add :key_p256dh, :string - add :key_auth, :string - add :data, :map + add(:user_id, references("users", on_delete: :delete_all)) + add(:token_id, references("oauth_tokens", on_delete: :delete_all)) + add(:endpoint, :string) + add(:key_p256dh, :string) + add(:key_auth, :string) + add(:data, :map) timestamps() end - create_if_not_exists index("push_subscriptions", [:user_id, :token_id], unique: true) + create_if_not_exists(index("push_subscriptions", [:user_id, :token_id], unique: true)) end end diff --git a/priv/repo/migrations/20180919060348_users_add_last_refreshed_at.exs b/priv/repo/migrations/20180919060348_users_add_last_refreshed_at.exs index 815177e05..16605cf7b 100644 --- a/priv/repo/migrations/20180919060348_users_add_last_refreshed_at.exs +++ b/priv/repo/migrations/20180919060348_users_add_last_refreshed_at.exs @@ -3,7 +3,7 @@ defmodule Pleroma.Repo.Migrations.UsersAddLastRefreshedAt do def change do alter table(:users) do - add :last_refreshed_at, :naive_datetime_usec + add(:last_refreshed_at, :naive_datetime_usec) end end end diff --git a/priv/repo/migrations/20181206125616_add_tags_to_users.exs b/priv/repo/migrations/20181206125616_add_tags_to_users.exs index 7d42a0fba..a46c0fc35 100644 --- a/priv/repo/migrations/20181206125616_add_tags_to_users.exs +++ b/priv/repo/migrations/20181206125616_add_tags_to_users.exs @@ -3,9 +3,9 @@ defmodule Pleroma.Repo.Migrations.AddTagsToUsers do def change do alter table(:users) do - add :tags, {:array, :string} + add(:tags, {:array, :string}) end - create_if_not_exists index(:users, [:tags], using: :gin) + create_if_not_exists(index(:users, [:tags], using: :gin)) end end diff --git a/priv/repo/migrations/20181214121049_add_bookmarks_to_users.exs b/priv/repo/migrations/20181214121049_add_bookmarks_to_users.exs index 55e97ae0e..6228f1bad 100644 --- a/priv/repo/migrations/20181214121049_add_bookmarks_to_users.exs +++ b/priv/repo/migrations/20181214121049_add_bookmarks_to_users.exs @@ -3,7 +3,7 @@ defmodule Pleroma.Repo.Migrations.AddBookmarksToUsers do def change do alter table(:users) do - add :bookmarks, {:array, :string}, null: false, default: [] + add(:bookmarks, {:array, :string}, null: false, default: []) end end end diff --git a/priv/repo/migrations/20181218172826_users_and_activities_flake_id.exs b/priv/repo/migrations/20181218172826_users_and_activities_flake_id.exs index a5b4c543d..c58d829af 100644 --- a/priv/repo/migrations/20181218172826_users_and_activities_flake_id.exs +++ b/priv/repo/migrations/20181218172826_users_and_activities_flake_id.exs @@ -16,32 +16,34 @@ def up do # Old serial int ids are transformed to 128bits with extra padding. # The application (in `Pleroma.FlakeId`) handles theses IDs properly as integers; to keep compatibility # with previously issued ids. - #execute "update activities set external_id = CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid);" - #execute "update users set external_id = CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid);" + # execute "update activities set external_id = CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid);" + # execute "update users set external_id = CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid);" clippy = start_clippy_heartbeats() # Lock both tables to avoid a running server to meddling with our transaction - execute "LOCK TABLE activities;" - execute "LOCK TABLE users;" + execute("LOCK TABLE activities;") + execute("LOCK TABLE users;") - execute """ + execute(""" ALTER TABLE activities DROP CONSTRAINT activities_pkey CASCADE, ALTER COLUMN id DROP default, ALTER COLUMN id SET DATA TYPE uuid USING CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid), ADD PRIMARY KEY (id); - """ + """) - execute """ + execute(""" ALTER TABLE users DROP CONSTRAINT users_pkey CASCADE, ALTER COLUMN id DROP default, ALTER COLUMN id SET DATA TYPE uuid USING CAST( LPAD( TO_HEX(id), 32, '0' ) AS uuid), ADD PRIMARY KEY (id); - """ + """) - execute "UPDATE users SET info = jsonb_set(info, '{pinned_activities}', array_to_json(ARRAY(select jsonb_array_elements_text(info->'pinned_activities')))::jsonb);" + execute( + "UPDATE users SET info = jsonb_set(info, '{pinned_activities}', array_to_json(ARRAY(select jsonb_array_elements_text(info->'pinned_activities')))::jsonb);" + ) # Fkeys: # Activities - Referenced by: @@ -56,18 +58,19 @@ def up do # TABLE "push_subscriptions" CONSTRAINT "push_subscriptions_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE # TABLE "websub_client_subscriptions" CONSTRAINT "websub_client_subscriptions_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) - execute """ + execute(""" ALTER TABLE notifications ALTER COLUMN activity_id SET DATA TYPE uuid USING CAST( LPAD( TO_HEX(activity_id), 32, '0' ) AS uuid), ADD CONSTRAINT notifications_activity_id_fkey FOREIGN KEY (activity_id) REFERENCES activities(id) ON DELETE CASCADE; - """ + """) - for table <- ~w(notifications filters lists oauth_authorizations oauth_tokens password_reset_tokens push_subscriptions websub_client_subscriptions) do - execute """ + for table <- + ~w(notifications filters lists oauth_authorizations oauth_tokens password_reset_tokens push_subscriptions websub_client_subscriptions) do + execute(""" ALTER TABLE #{table} ALTER COLUMN user_id SET DATA TYPE uuid USING CAST( LPAD( TO_HEX(user_id), 32, '0' ) AS uuid), ADD CONSTRAINT #{table}_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; - """ + """) end flush() @@ -78,41 +81,50 @@ def up do def down, do: :ok defp start_clippy_heartbeats() do - count = from(a in "activities", select: count(a.id)) |> Repo.one! + count = from(a in "activities", select: count(a.id)) |> Repo.one!() if count > 5000 do heartbeat_interval = :timer.minutes(2) + :timer.seconds(30) - all_tips = Clippy.tips() ++ [ - "The migration is still running, maybe it's time for another “tea”?", - "Happy rabbits practice a cute behavior known as a\n“binky:” they jump up in the air\nand twist\nand spin around!", - "Nothing and everything.\n\nI still work.", - "Pleroma runs on a Raspberry Pi!\n\n … but this migration will take forever if you\nactually run on a raspberry pi", - "Status? Stati? Post? Note? Toot?\nRepeat? Reboost? Boost? Retweet? Retoot??\n\nI-I'm confused.", - ] - heartbeat = fn(heartbeat, runs, all_tips, tips) -> - tips = if Integer.is_even(runs) do - tips = if tips == [], do: all_tips, else: tips - [tip | tips] = Enum.shuffle(tips) - Clippy.puts(tip) - tips - else - IO.puts "\n -- #{DateTime.to_string(DateTime.utc_now())} Migration still running, please wait…\n" - tips - end + all_tips = + Clippy.tips() ++ + [ + "The migration is still running, maybe it's time for another “tea”?", + "Happy rabbits practice a cute behavior known as a\n“binky:” they jump up in the air\nand twist\nand spin around!", + "Nothing and everything.\n\nI still work.", + "Pleroma runs on a Raspberry Pi!\n\n … but this migration will take forever if you\nactually run on a raspberry pi", + "Status? Stati? Post? Note? Toot?\nRepeat? Reboost? Boost? Retweet? Retoot??\n\nI-I'm confused." + ] + + heartbeat = fn heartbeat, runs, all_tips, tips -> + tips = + if Integer.is_even(runs) do + tips = if tips == [], do: all_tips, else: tips + [tip | tips] = Enum.shuffle(tips) + Clippy.puts(tip) + tips + else + IO.puts( + "\n -- #{DateTime.to_string(DateTime.utc_now())} Migration still running, please wait…\n" + ) + + tips + end + :timer.sleep(heartbeat_interval) heartbeat.(heartbeat, runs + 1, all_tips, tips) end - Clippy.puts [ + Clippy.puts([ [:red, :bright, "It looks like you are running an older instance!"], [""], [:bright, "This migration may take a long time", :reset, " -- so you probably should"], ["go drink a cofe, or a tea, or a beer, a whiskey, a vodka,"], ["while it runs to deal with your temporary fediverse pause!"] - ] + ]) + :timer.sleep(heartbeat_interval) - spawn_link(fn() -> heartbeat.(heartbeat, 1, all_tips, []) end) + spawn_link(fn -> heartbeat.(heartbeat, 1, all_tips, []) end) end end @@ -120,8 +132,7 @@ defp stop_clippy_heartbeats(pid) do if pid do Process.unlink(pid) Process.exit(pid, :kill) - Clippy.puts [[:green, :bright, "Hurray!!", "", "", "Migration completed!"]] + Clippy.puts([[:green, :bright, "Hurray!!", "", "", "Migration completed!"]]) end end - end diff --git a/priv/repo/migrations/20190109152453_add_visibility_function.exs b/priv/repo/migrations/20190109152453_add_visibility_function.exs index b6a4e752b..43d1074aa 100644 --- a/priv/repo/migrations/20190109152453_add_visibility_function.exs +++ b/priv/repo/migrations/20190109152453_add_visibility_function.exs @@ -43,6 +43,8 @@ def down do ) ) - execute("drop function if exists activity_visibility(actor varchar, recipients varchar[], data jsonb)") + execute( + "drop function if exists activity_visibility(actor varchar, recipients varchar[], data jsonb)" + ) end end diff --git a/priv/repo/migrations/20190115085500_create_user_fts_index.exs b/priv/repo/migrations/20190115085500_create_user_fts_index.exs index cff975318..0c0c512d3 100644 --- a/priv/repo/migrations/20190115085500_create_user_fts_index.exs +++ b/priv/repo/migrations/20190115085500_create_user_fts_index.exs @@ -2,16 +2,18 @@ defmodule Pleroma.Repo.Migrations.CreateUserFtsIndex do use Ecto.Migration def change do - create_if_not_exists index( - :users, - [ - """ - (setweight(to_tsvector('simple', regexp_replace(nickname, '\\W', ' ', 'g')), 'A') || - setweight(to_tsvector('simple', regexp_replace(coalesce(name, ''), '\\W', ' ', 'g')), 'B')) - """ - ], - name: :users_fts_index, - using: :gin - ) + create_if_not_exists( + index( + :users, + [ + """ + (setweight(to_tsvector('simple', regexp_replace(nickname, '\\W', ' ', 'g')), 'A') || + setweight(to_tsvector('simple', regexp_replace(coalesce(name, ''), '\\W', ' ', 'g')), 'B')) + """ + ], + name: :users_fts_index, + using: :gin + ) + ) end end diff --git a/priv/repo/migrations/20190122153157_update_activity_visibility.exs b/priv/repo/migrations/20190122153157_update_activity_visibility.exs index 30075137c..9e29571ee 100644 --- a/priv/repo/migrations/20190122153157_update_activity_visibility.exs +++ b/priv/repo/migrations/20190122153157_update_activity_visibility.exs @@ -27,10 +27,8 @@ def up do """ execute(definition) - end def down do - end end diff --git a/priv/repo/migrations/20190123092341_users_add_is_admin_index.exs b/priv/repo/migrations/20190123092341_users_add_is_admin_index.exs index 25f248c59..f42d46427 100644 --- a/priv/repo/migrations/20190123092341_users_add_is_admin_index.exs +++ b/priv/repo/migrations/20190123092341_users_add_is_admin_index.exs @@ -2,6 +2,8 @@ defmodule Pleroma.Repo.Migrations.UsersAddIsAdminIndex do use Ecto.Migration def change do - create_if_not_exists(index(:users, ["(info->'is_admin')"], name: :users_is_admin_index, using: :gin)) + create_if_not_exists( + index(:users, ["(info->'is_admin')"], name: :users_is_admin_index, using: :gin) + ) end end diff --git a/priv/repo/migrations/20190123125546_create_instances.exs b/priv/repo/migrations/20190123125546_create_instances.exs index a9b356bc3..9438736ba 100644 --- a/priv/repo/migrations/20190123125546_create_instances.exs +++ b/priv/repo/migrations/20190123125546_create_instances.exs @@ -3,13 +3,13 @@ defmodule Pleroma.Repo.Migrations.CreateInstances do def change do create_if_not_exists table(:instances) do - add :host, :string - add :unreachable_since, :naive_datetime_usec + add(:host, :string) + add(:unreachable_since, :naive_datetime_usec) timestamps() end - create_if_not_exists unique_index(:instances, [:host]) - create_if_not_exists index(:instances, [:unreachable_since]) + create_if_not_exists(unique_index(:instances, [:host])) + create_if_not_exists(index(:instances, [:unreachable_since])) end end diff --git a/priv/repo/migrations/20190124131141_update_activity_visibility_again.exs b/priv/repo/migrations/20190124131141_update_activity_visibility_again.exs index 0519a5143..a42e4cad9 100644 --- a/priv/repo/migrations/20190124131141_update_activity_visibility_again.exs +++ b/priv/repo/migrations/20190124131141_update_activity_visibility_again.exs @@ -27,11 +27,8 @@ def up do """ execute(definition) - end def down do - end - end diff --git a/priv/repo/migrations/20190127151220_add_activities_likes_index.exs b/priv/repo/migrations/20190127151220_add_activities_likes_index.exs index b1822d265..115b12491 100644 --- a/priv/repo/migrations/20190127151220_add_activities_likes_index.exs +++ b/priv/repo/migrations/20190127151220_add_activities_likes_index.exs @@ -3,6 +3,12 @@ defmodule Pleroma.Repo.Migrations.AddActivitiesLikesIndex do @disable_ddl_transaction true def change do - create index(:activities, ["((data #> '{\"object\",\"likes\"}'))"], concurrently: true, name: :activities_likes, using: :gin) + create( + index(:activities, ["((data #> '{\"object\",\"likes\"}'))"], + concurrently: true, + name: :activities_likes, + using: :gin + ) + ) end end diff --git a/priv/repo/migrations/20190203185340_split_hide_network.exs b/priv/repo/migrations/20190203185340_split_hide_network.exs index 8b7a9151b..fb677f68a 100644 --- a/priv/repo/migrations/20190203185340_split_hide_network.exs +++ b/priv/repo/migrations/20190203185340_split_hide_network.exs @@ -2,9 +2,17 @@ defmodule Pleroma.Repo.Migrations.SplitHideNetwork do use Ecto.Migration def up do - execute("UPDATE users SET info = jsonb_set(info, '{hide_network}'::text[], 'false'::jsonb) WHERE NOT(info::jsonb ? 'hide_network') AND local") - execute("UPDATE users SET info = jsonb_set(info, '{hide_followings}'::text[], info->'hide_network') WHERE local") - execute("UPDATE users SET info = jsonb_set(info, '{hide_followers}'::text[], info->'hide_network') WHERE local") + execute( + "UPDATE users SET info = jsonb_set(info, '{hide_network}'::text[], 'false'::jsonb) WHERE NOT(info::jsonb ? 'hide_network') AND local" + ) + + execute( + "UPDATE users SET info = jsonb_set(info, '{hide_followings}'::text[], info->'hide_network') WHERE local" + ) + + execute( + "UPDATE users SET info = jsonb_set(info, '{hide_followers}'::text[], info->'hide_network') WHERE local" + ) end def down do diff --git a/priv/repo/migrations/20190205114625_create_thread_mutes.exs b/priv/repo/migrations/20190205114625_create_thread_mutes.exs index baaf07253..df9eb7677 100644 --- a/priv/repo/migrations/20190205114625_create_thread_mutes.exs +++ b/priv/repo/migrations/20190205114625_create_thread_mutes.exs @@ -3,10 +3,10 @@ defmodule Pleroma.Repo.Migrations.CreateThreadMutes do def change do create_if_not_exists table(:thread_mutes) do - add :user_id, references(:users, type: :uuid, on_delete: :delete_all) - add :context, :string + add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) + add(:context, :string) end - create_if_not_exists unique_index(:thread_mutes, [:user_id, :context], name: :unique_index) + create_if_not_exists(unique_index(:thread_mutes, [:user_id, :context], name: :unique_index)) end end diff --git a/priv/repo/migrations/20190208131753_add_scopes_to_o_auth_entities.exs b/priv/repo/migrations/20190208131753_add_scopes_to_o_auth_entities.exs index 4efbebc4d..ad93bfce2 100644 --- a/priv/repo/migrations/20190208131753_add_scopes_to_o_auth_entities.exs +++ b/priv/repo/migrations/20190208131753_add_scopes_to_o_auth_entities.exs @@ -4,7 +4,7 @@ defmodule Pleroma.Repo.Migrations.AddScopeSToOAuthEntities do def change do for t <- [:oauth_authorizations, :oauth_tokens] do alter table(t) do - add :scopes, {:array, :string}, default: [], null: false + add(:scopes, {:array, :string}, default: [], null: false) end end end diff --git a/priv/repo/migrations/20190213185503_change_apps_scopes_to_varchar_array.exs b/priv/repo/migrations/20190213185503_change_apps_scopes_to_varchar_array.exs index 72decd401..eb6fcb012 100644 --- a/priv/repo/migrations/20190213185503_change_apps_scopes_to_varchar_array.exs +++ b/priv/repo/migrations/20190213185503_change_apps_scopes_to_varchar_array.exs @@ -4,14 +4,20 @@ defmodule Pleroma.Repo.Migrations.ChangeAppsScopesToVarcharArray do @alter_apps_scopes "ALTER TABLE apps ALTER COLUMN scopes" def up do - execute "#{@alter_apps_scopes} TYPE varchar(255)[] USING string_to_array(scopes, ',')::varchar(255)[];" - execute "#{@alter_apps_scopes} SET DEFAULT ARRAY[]::character varying[];" - execute "#{@alter_apps_scopes} SET NOT NULL;" + execute( + "#{@alter_apps_scopes} TYPE varchar(255)[] USING string_to_array(scopes, ',')::varchar(255)[];" + ) + + execute("#{@alter_apps_scopes} SET DEFAULT ARRAY[]::character varying[];") + execute("#{@alter_apps_scopes} SET NOT NULL;") end def down do - execute "#{@alter_apps_scopes} DROP NOT NULL;" - execute "#{@alter_apps_scopes} DROP DEFAULT;" - execute "#{@alter_apps_scopes} TYPE varchar(255) USING array_to_string(scopes, ',')::varchar(255);" + execute("#{@alter_apps_scopes} DROP NOT NULL;") + execute("#{@alter_apps_scopes} DROP DEFAULT;") + + execute( + "#{@alter_apps_scopes} TYPE varchar(255) USING array_to_string(scopes, ',')::varchar(255);" + ) end end diff --git a/priv/repo/migrations/20190213185600_data_migration_populate_o_auth_scopes.exs b/priv/repo/migrations/20190213185600_data_migration_populate_o_auth_scopes.exs index 7afbcbd76..ef5b35125 100644 --- a/priv/repo/migrations/20190213185600_data_migration_populate_o_auth_scopes.exs +++ b/priv/repo/migrations/20190213185600_data_migration_populate_o_auth_scopes.exs @@ -3,7 +3,7 @@ defmodule Pleroma.Repo.Migrations.DataMigrationPopulateOAuthScopes do def up do for t <- [:oauth_authorizations, :oauth_tokens] do - execute "UPDATE #{t} SET scopes = apps.scopes FROM apps WHERE #{t}.app_id = apps.id;" + execute("UPDATE #{t} SET scopes = apps.scopes FROM apps WHERE #{t}.app_id = apps.id;") end end diff --git a/priv/repo/migrations/20190222104808_data_migration_normalize_scopes.exs b/priv/repo/migrations/20190222104808_data_migration_normalize_scopes.exs index d44e5096b..92ab9bd2c 100644 --- a/priv/repo/migrations/20190222104808_data_migration_normalize_scopes.exs +++ b/priv/repo/migrations/20190222104808_data_migration_normalize_scopes.exs @@ -3,7 +3,7 @@ defmodule Pleroma.Repo.Migrations.DataMigrationNormalizeScopes do def up do for t <- [:apps, :oauth_authorizations, :oauth_tokens] do - execute "UPDATE #{t} SET scopes = string_to_array(array_to_string(scopes, ' '), ' ');" + execute("UPDATE #{t} SET scopes = string_to_array(array_to_string(scopes, ' '), ' ');") end end diff --git a/priv/repo/migrations/20190301101154_add_default_tags_to_user.exs b/priv/repo/migrations/20190301101154_add_default_tags_to_user.exs index faeb8f1c6..ea0947852 100644 --- a/priv/repo/migrations/20190301101154_add_default_tags_to_user.exs +++ b/priv/repo/migrations/20190301101154_add_default_tags_to_user.exs @@ -2,7 +2,7 @@ defmodule Pleroma.Repo.Migrations.AddDefaultTagsToUser do use Ecto.Migration def up do - execute "UPDATE users SET tags = array[]::varchar[] where tags IS NULL" + execute("UPDATE users SET tags = array[]::varchar[] where tags IS NULL") end def down, do: :noop diff --git a/priv/repo/migrations/20190303120636_update_user_note_counters.exs b/priv/repo/migrations/20190303120636_update_user_note_counters.exs index 54e68f7c9..95dbd012f 100644 --- a/priv/repo/migrations/20190303120636_update_user_note_counters.exs +++ b/priv/repo/migrations/20190303120636_update_user_note_counters.exs @@ -4,7 +4,7 @@ defmodule Pleroma.Repo.Migrations.UpdateUserNoteCounters do @public "https://www.w3.org/ns/activitystreams#Public" def up do - execute """ + execute(""" WITH public_note_count AS ( SELECT data->>'actor' AS actor, @@ -19,11 +19,11 @@ def up do SET "info" = jsonb_set(u.info, '{note_count}', o.count::varchar::jsonb, true) FROM public_note_count AS o WHERE u.ap_id = o.actor - """ + """) end def down do - execute """ + execute(""" WITH public_note_count AS ( SELECT data->>'actor' AS actor, @@ -36,6 +36,6 @@ def down do SET "info" = jsonb_set(u.info, '{note_count}', o.count::varchar::jsonb, true) FROM public_note_count AS o WHERE u.ap_id = o.actor - """ + """) end end diff --git a/priv/repo/migrations/20190315101315_create_registrations.exs b/priv/repo/migrations/20190315101315_create_registrations.exs index 34a390a93..d705a499e 100644 --- a/priv/repo/migrations/20190315101315_create_registrations.exs +++ b/priv/repo/migrations/20190315101315_create_registrations.exs @@ -3,16 +3,16 @@ defmodule Pleroma.Repo.Migrations.CreateRegistrations do def change do create_if_not_exists table(:registrations, primary_key: false) do - add :id, :uuid, primary_key: true - add :user_id, references(:users, type: :uuid, on_delete: :delete_all) - add :provider, :string - add :uid, :string - add :info, :map, default: %{} + add(:id, :uuid, primary_key: true) + add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) + add(:provider, :string) + add(:uid, :string) + add(:info, :map, default: %{}) timestamps() end - create_if_not_exists unique_index(:registrations, [:provider, :uid]) - create_if_not_exists unique_index(:registrations, [:user_id, :provider, :uid]) + create_if_not_exists(unique_index(:registrations, [:provider, :uid])) + create_if_not_exists(unique_index(:registrations, [:user_id, :provider, :uid])) end end diff --git a/priv/repo/migrations/20190325185009_create_notification_id_index.exs b/priv/repo/migrations/20190325185009_create_notification_id_index.exs index 01cb30559..7209c16a9 100644 --- a/priv/repo/migrations/20190325185009_create_notification_id_index.exs +++ b/priv/repo/migrations/20190325185009_create_notification_id_index.exs @@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.CreateNotificationIdIndex do use Ecto.Migration def change do - create_if_not_exists index(:notifications, ["id desc nulls last"]) + create_if_not_exists(index(:notifications, ["id desc nulls last"])) end end diff --git a/priv/repo/migrations/20190405160700_add_index_on_subscribers.exs b/priv/repo/migrations/20190405160700_add_index_on_subscribers.exs index 460dafb1b..bbf47f72c 100644 --- a/priv/repo/migrations/20190405160700_add_index_on_subscribers.exs +++ b/priv/repo/migrations/20190405160700_add_index_on_subscribers.exs @@ -3,6 +3,12 @@ defmodule Pleroma.Repo.Migrations.AddIndexOnSubscribers do @disable_ddl_transaction true def change do - create index(:users, ["(info->'subscribers')"], name: :users_subscribers_index, using: :gin, concurrently: true) + create( + index(:users, ["(info->'subscribers')"], + name: :users_subscribers_index, + using: :gin, + concurrently: true + ) + ) end end diff --git a/priv/repo/migrations/20190408123347_create_conversations.exs b/priv/repo/migrations/20190408123347_create_conversations.exs index 7b7d89da7..d75459e82 100644 --- a/priv/repo/migrations/20190408123347_create_conversations.exs +++ b/priv/repo/migrations/20190408123347_create_conversations.exs @@ -19,8 +19,8 @@ def change do timestamps() end - create_if_not_exists index(:conversation_participations, [:conversation_id]) - create_if_not_exists unique_index(:conversation_participations, [:user_id, :conversation_id]) - create_if_not_exists unique_index(:conversations, [:ap_id]) + create_if_not_exists(index(:conversation_participations, [:conversation_id])) + create_if_not_exists(unique_index(:conversation_participations, [:user_id, :conversation_id])) + create_if_not_exists(unique_index(:conversations, [:ap_id])) end end diff --git a/priv/repo/migrations/20190410152859_add_participation_updated_at_index.exs b/priv/repo/migrations/20190410152859_add_participation_updated_at_index.exs index b5ca2fc0f..e22c6e57d 100644 --- a/priv/repo/migrations/20190410152859_add_participation_updated_at_index.exs +++ b/priv/repo/migrations/20190410152859_add_participation_updated_at_index.exs @@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddParticipationUpdatedAtIndex do use Ecto.Migration def change do - create_if_not_exists index(:conversation_participations, ["updated_at desc"]) + create_if_not_exists(index(:conversation_participations, ["updated_at desc"])) end end diff --git a/priv/repo/migrations/20190411094120_add_index_on_user_info_deactivated.exs b/priv/repo/migrations/20190411094120_add_index_on_user_info_deactivated.exs index c19427f12..374e2323d 100644 --- a/priv/repo/migrations/20190411094120_add_index_on_user_info_deactivated.exs +++ b/priv/repo/migrations/20190411094120_add_index_on_user_info_deactivated.exs @@ -2,6 +2,8 @@ defmodule Pleroma.Repo.Migrations.AddIndexOnUserInfoDeactivated do use Ecto.Migration def change do - create_if_not_exists(index(:users, ["(info->'deactivated')"], name: :users_deactivated_index, using: :gin)) + create_if_not_exists( + index(:users, ["(info->'deactivated')"], name: :users_deactivated_index, using: :gin) + ) end end diff --git a/priv/repo/migrations/20190414125034_migrate_old_bookmarks.exs b/priv/repo/migrations/20190414125034_migrate_old_bookmarks.exs index ce4590954..f3928a149 100644 --- a/priv/repo/migrations/20190414125034_migrate_old_bookmarks.exs +++ b/priv/repo/migrations/20190414125034_migrate_old_bookmarks.exs @@ -18,7 +18,7 @@ def up do |> Enum.each(fn %{id: user_id, bookmarks: bookmarks} -> Enum.each(bookmarks, fn ap_id -> activity = Activity.get_create_by_object_ap_id(ap_id) - unless is_nil(activity), do: {:ok, _} = Bookmark.create(user_id, activity.id) + unless is_nil(activity), do: {:ok, _} = Bookmark.create(user_id, activity.id) end) end) @@ -29,7 +29,7 @@ def up do def down do alter table(:users) do - add :bookmarks, {:array, :string}, null: false, default: [] + add(:bookmarks, {:array, :string}, null: false, default: []) end end end diff --git a/priv/repo/migrations/20190501125843_add_fts_index_to_objects.exs b/priv/repo/migrations/20190501125843_add_fts_index_to_objects.exs index d4de51691..41630bace 100644 --- a/priv/repo/migrations/20190501125843_add_fts_index_to_objects.exs +++ b/priv/repo/migrations/20190501125843_add_fts_index_to_objects.exs @@ -2,7 +2,18 @@ defmodule Pleroma.Repo.Migrations.AddFTSIndexToObjects do use Ecto.Migration def change do - drop_if_exists index(:activities, ["(to_tsvector('english', data->'object'->>'content'))"], using: :gin, name: :activities_fts) - create_if_not_exists index(:objects, ["(to_tsvector('english', data->>'content'))"], using: :gin, name: :objects_fts) + drop_if_exists( + index(:activities, ["(to_tsvector('english', data->'object'->>'content'))"], + using: :gin, + name: :activities_fts + ) + ) + + create_if_not_exists( + index(:objects, ["(to_tsvector('english', data->>'content'))"], + using: :gin, + name: :objects_fts + ) + ) end end diff --git a/priv/repo/migrations/20190511191044_set_default_state_to_reports.exs b/priv/repo/migrations/20190511191044_set_default_state_to_reports.exs index 0d3d253b6..ab1351d56 100644 --- a/priv/repo/migrations/20190511191044_set_default_state_to_reports.exs +++ b/priv/repo/migrations/20190511191044_set_default_state_to_reports.exs @@ -2,18 +2,18 @@ defmodule Pleroma.Repo.Migrations.SetDefaultStateToReports do use Ecto.Migration def up do - execute """ + execute(""" UPDATE activities AS a SET data = jsonb_set(data, '{state}', '"open"', true) WHERE data->>'type' = 'Flag' - """ + """) end def down do - execute """ + execute(""" UPDATE activities AS a SET data = data #- '{state}' WHERE data->>'type' = 'Flag' - """ + """) end end diff --git a/priv/repo/migrations/20190513175809_change_hide_column_in_filter_table.exs b/priv/repo/migrations/20190513175809_change_hide_column_in_filter_table.exs index 246b70cfb..8135ab178 100644 --- a/priv/repo/migrations/20190513175809_change_hide_column_in_filter_table.exs +++ b/priv/repo/migrations/20190513175809_change_hide_column_in_filter_table.exs @@ -3,13 +3,13 @@ defmodule Pleroma.Repo.Migrations.ChangeHideColumnInFilterTable do def up do alter table(:filters) do - modify :hide, :boolean, default: false + modify(:hide, :boolean, default: false) end end def down do alter table(:filters) do - modify :hide, :boolean + modify(:hide, :boolean) end end end diff --git a/priv/repo/migrations/20190603162018_add_object_in_reply_to_index.exs b/priv/repo/migrations/20190603162018_add_object_in_reply_to_index.exs index df4ac7782..faed5e31b 100644 --- a/priv/repo/migrations/20190603162018_add_object_in_reply_to_index.exs +++ b/priv/repo/migrations/20190603162018_add_object_in_reply_to_index.exs @@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddObjectInReplyToIndex do use Ecto.Migration def change do - create index(:objects, ["(data->>'inReplyTo')"], name: :objects_in_reply_to_index) + create(index(:objects, ["(data->>'inReplyTo')"], name: :objects_in_reply_to_index)) end end diff --git a/priv/repo/migrations/20190603173419_add_tag_index_to_objects.exs b/priv/repo/migrations/20190603173419_add_tag_index_to_objects.exs index 93d57a249..9ba95917c 100644 --- a/priv/repo/migrations/20190603173419_add_tag_index_to_objects.exs +++ b/priv/repo/migrations/20190603173419_add_tag_index_to_objects.exs @@ -2,7 +2,10 @@ defmodule Pleroma.Repo.Migrations.AddTagIndexToObjects do use Ecto.Migration def change do - drop_if_exists index(:activities, ["(data #> '{\"object\",\"tag\"}')"], using: :gin, name: :activities_tags) - create_if_not_exists index(:objects, ["(data->'tag')"], using: :gin, name: :objects_tags) + drop_if_exists( + index(:activities, ["(data #> '{\"object\",\"tag\"}')"], using: :gin, name: :activities_tags) + ) + + create_if_not_exists(index(:objects, ["(data->'tag')"], using: :gin, name: :objects_tags)) end end diff --git a/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs b/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs index 50669902e..bc4e828cc 100644 --- a/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs +++ b/priv/repo/migrations/20190711042024_copy_muted_to_muted_notifications.exs @@ -3,22 +3,8 @@ defmodule Pleroma.Repo.Migrations.CopyMutedToMutedNotifications do alias Pleroma.User def change do - query = - User.Query.build(%{ - local: true, - active: true, - order_by: :id - }) - - Pleroma.Repo.stream(query) - |> Enum.each(fn - %{info: %{mutes: mutes} = info} = user -> - info_cng = - Ecto.Changeset.cast(info, %{muted_notifications: mutes}, [:muted_notifications]) - - Ecto.Changeset.change(user) - |> Ecto.Changeset.put_embed(:info, info_cng) - |> Pleroma.Repo.update() - end) + execute( + "update users set info = jsonb_set(info, '{muted_notifications}', info->'mutes', true) where local = true" + ) end end diff --git a/priv/repo/migrations/20190801154554_create_conversation_participation_recipient_ships.exs b/priv/repo/migrations/20190801154554_create_conversation_participation_recipient_ships.exs index c6e3469d5..59cbe25ad 100644 --- a/priv/repo/migrations/20190801154554_create_conversation_participation_recipient_ships.exs +++ b/priv/repo/migrations/20190801154554_create_conversation_participation_recipient_ships.exs @@ -7,7 +7,7 @@ def change do add(:participation_id, references(:conversation_participations, on_delete: :delete_all)) end - create_if_not_exists index(:conversation_participation_recipient_ships, [:user_id]) - create_if_not_exists index(:conversation_participation_recipient_ships, [:participation_id]) + create_if_not_exists(index(:conversation_participation_recipient_ships, [:user_id])) + create_if_not_exists(index(:conversation_participation_recipient_ships, [:participation_id])) end end diff --git a/priv/repo/migrations/20190823000549_add_likes_index_to_objects.exs b/priv/repo/migrations/20190823000549_add_likes_index_to_objects.exs index 13f3d6e83..c410dcdc2 100644 --- a/priv/repo/migrations/20190823000549_add_likes_index_to_objects.exs +++ b/priv/repo/migrations/20190823000549_add_likes_index_to_objects.exs @@ -2,6 +2,6 @@ defmodule Pleroma.Repo.Migrations.AddLikesIndexToObjects do use Ecto.Migration def change do - create_if_not_exists index(:objects, ["(data->'likes')"], using: :gin, name: :objects_likes) + create_if_not_exists(index(:objects, ["(data->'likes')"], using: :gin, name: :objects_likes)) end end diff --git a/priv/repo/migrations/20190912065617_create_deliveries.exs b/priv/repo/migrations/20190912065617_create_deliveries.exs index 79071a799..ac2832a77 100644 --- a/priv/repo/migrations/20190912065617_create_deliveries.exs +++ b/priv/repo/migrations/20190912065617_create_deliveries.exs @@ -6,7 +6,8 @@ def change do add(:object_id, references(:objects, type: :id), null: false) add(:user_id, references(:users, type: :uuid, on_delete: :delete_all), null: false) end - create_if_not_exists index(:deliveries, :object_id, name: :deliveries_object_id) + + create_if_not_exists(index(:deliveries, :object_id, name: :deliveries_object_id)) create_if_not_exists(unique_index(:deliveries, [:user_id, :object_id])) end end diff --git a/priv/repo/migrations/20190929201536_drop_subscription_if_exists.exs b/priv/repo/migrations/20190929201536_drop_subscription_if_exists.exs index bbf70f78b..8bd2a98bb 100644 --- a/priv/repo/migrations/20190929201536_drop_subscription_if_exists.exs +++ b/priv/repo/migrations/20190929201536_drop_subscription_if_exists.exs @@ -2,7 +2,6 @@ defmodule Pleroma.Repo.Migrations.DropSubscriptionIfExists do use Ecto.Migration def change do - end def up do @@ -10,6 +9,7 @@ def up do drop_if_exists(index(:subscription_notifications, ["id desc nulls last"])) drop_if_exists(table(:subscription_notifications)) end + def down do :ok end diff --git a/priv/repo/migrations/20191005165212_add_unread_conversation_count_to_user_info.exs b/priv/repo/migrations/20191005165212_add_unread_conversation_count_to_user_info.exs new file mode 100644 index 000000000..2aa1a012c --- /dev/null +++ b/priv/repo/migrations/20191005165212_add_unread_conversation_count_to_user_info.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.AddUnreadConversationCountToUserInfo do + use Ecto.Migration + + def up do + execute(""" + update users set info = jsonb_set(info, '{unread_conversation_count}', 0::varchar::jsonb, true) where local=true + """) + end + + def down, do: :ok +end diff --git a/priv/repo/migrations/20191006123824_add_keys_column.exs b/priv/repo/migrations/20191006123824_add_keys_column.exs new file mode 100644 index 000000000..4114ba416 --- /dev/null +++ b/priv/repo/migrations/20191006123824_add_keys_column.exs @@ -0,0 +1,9 @@ +defmodule Pleroma.Repo.Migrations.AddKeysColumn do + use Ecto.Migration + + def change do + alter table("users") do + add_if_not_exists(:keys, :text) + end + end +end diff --git a/priv/repo/migrations/20191006135457_move_keys_to_separate_column.exs b/priv/repo/migrations/20191006135457_move_keys_to_separate_column.exs new file mode 100644 index 000000000..cb8d4ae9e --- /dev/null +++ b/priv/repo/migrations/20191006135457_move_keys_to_separate_column.exs @@ -0,0 +1,10 @@ +defmodule Pleroma.Repo.Migrations.MoveKeysToSeparateColumn do + use Ecto.Migration + + def change do + execute( + "update users set keys = info->>'keys' where local", + "update users set info = jsonb_set(info, '{keys}'::text[], to_jsonb(keys)) where local" + ) + end +end diff --git a/priv/static/index.html b/priv/static/index.html index c0952e09c..fc5eee901 100644 --- a/priv/static/index.html +++ b/priv/static/index.html @@ -1 +1 @@ -Pleroma
\ No newline at end of file +Pleroma
\ No newline at end of file diff --git a/priv/static/schemas/litepub-0.1.jsonld b/priv/static/schemas/litepub-0.1.jsonld index f01c2c33a..1cfcb7ec7 100644 --- a/priv/static/schemas/litepub-0.1.jsonld +++ b/priv/static/schemas/litepub-0.1.jsonld @@ -14,9 +14,8 @@ "discoverable": "toot:discoverable", "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", "ostatus": "http://ostatus.org#", - "schema": "http://schema.org", + "schema": "http://schema.org#", "toot": "http://joinmastodon.org/ns#", - "totalItems": "as:totalItems", "value": "schema:value", "sensitive": "as:sensitive", "litepub": "http://litepub.social/ns#", @@ -28,10 +27,6 @@ "oauthRegistrationEndpoint": { "@id": "litepub:oauthRegistrationEndpoint", "@type": "@id" - }, - "uploadMedia": { - "@id": "litepub:uploadMedia", - "@type": "@id" } } ] diff --git a/priv/static/static/css/app.cb3673e4b661fd9526ea.css b/priv/static/static/css/app.4e8e80a2f95232cff399.css similarity index 82% rename from priv/static/static/css/app.cb3673e4b661fd9526ea.css rename to priv/static/static/css/app.4e8e80a2f95232cff399.css index e083f12c8..ca3d4e41f 100644 Binary files a/priv/static/static/css/app.cb3673e4b661fd9526ea.css and b/priv/static/static/css/app.4e8e80a2f95232cff399.css differ diff --git a/priv/static/static/css/app.4e8e80a2f95232cff399.css.map b/priv/static/static/css/app.4e8e80a2f95232cff399.css.map new file mode 100644 index 000000000..dc2c92ced --- /dev/null +++ b/priv/static/static/css/app.4e8e80a2f95232cff399.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///./src/hocs/with_load_more/with_load_more.scss","webpack:///./src/components/tab_switcher/tab_switcher.scss","webpack:///./src/hocs/with_subscription/with_subscription.scss"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;ACTA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;AClFA;AACA;AACA;AACA;AACA;AACA;AACA,C","file":"static/css/app.4e8e80a2f95232cff399.css","sourcesContent":[".with-load-more-footer {\n padding: 10px;\n text-align: center;\n border-top: 1px solid;\n border-top-color: #222;\n border-top-color: var(--border, #222);\n}\n.with-load-more-footer .error {\n font-size: 14px;\n}",".tab-switcher {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n.tab-switcher .contents {\n -ms-flex: 1 0 auto;\n flex: 1 0 auto;\n min-height: 0px;\n}\n.tab-switcher .contents .hidden {\n display: none;\n}\n.tab-switcher .contents.scrollable-tabs {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n overflow-y: auto;\n}\n.tab-switcher .tabs {\n display: -ms-flexbox;\n display: flex;\n position: relative;\n width: 100%;\n overflow-y: hidden;\n overflow-x: auto;\n padding-top: 5px;\n box-sizing: border-box;\n}\n.tab-switcher .tabs::after, .tab-switcher .tabs::before {\n display: block;\n content: \"\";\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}\n.tab-switcher .tabs .tab-wrapper {\n height: 28px;\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n}\n.tab-switcher .tabs .tab-wrapper .tab {\n width: 100%;\n min-width: 1px;\n position: relative;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n padding: 6px 1em;\n padding-bottom: 99px;\n margin-bottom: -93px;\n white-space: nowrap;\n}\n.tab-switcher .tabs .tab-wrapper .tab:not(.active) {\n z-index: 4;\n}\n.tab-switcher .tabs .tab-wrapper .tab:not(.active):hover {\n z-index: 6;\n}\n.tab-switcher .tabs .tab-wrapper .tab.active {\n background: transparent;\n z-index: 5;\n}\n.tab-switcher .tabs .tab-wrapper .tab img {\n max-height: 26px;\n vertical-align: top;\n margin-top: -5px;\n}\n.tab-switcher .tabs .tab-wrapper:not(.active)::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 7;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}",".with-subscription-loading {\n padding: 10px;\n text-align: center;\n}\n.with-subscription-loading .error {\n font-size: 14px;\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/priv/static/static/css/app.cb3673e4b661fd9526ea.css.map b/priv/static/static/css/app.cb3673e4b661fd9526ea.css.map deleted file mode 100644 index 8cecb0901..000000000 --- a/priv/static/static/css/app.cb3673e4b661fd9526ea.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///./src/components/tab_switcher/tab_switcher.scss","webpack:///./src/hocs/with_load_more/with_load_more.scss","webpack:///./src/hocs/with_subscription/with_subscription.scss"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;AClEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;ACTA;AACA;AACA;AACA;AACA;AACA;AACA,C","file":"static/css/app.cb3673e4b661fd9526ea.css","sourcesContent":[".tab-switcher .contents .hidden {\n display: none;\n}\n.tab-switcher .tabs {\n display: -ms-flexbox;\n display: flex;\n position: relative;\n width: 100%;\n overflow-y: hidden;\n overflow-x: auto;\n padding-top: 5px;\n box-sizing: border-box;\n}\n.tab-switcher .tabs::after, .tab-switcher .tabs::before {\n display: block;\n content: \"\";\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}\n.tab-switcher .tabs .tab-wrapper {\n height: 28px;\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n}\n.tab-switcher .tabs .tab-wrapper .tab {\n width: 100%;\n min-width: 1px;\n position: relative;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n padding: 6px 1em;\n padding-bottom: 99px;\n margin-bottom: -93px;\n white-space: nowrap;\n}\n.tab-switcher .tabs .tab-wrapper .tab:not(.active) {\n z-index: 4;\n}\n.tab-switcher .tabs .tab-wrapper .tab:not(.active):hover {\n z-index: 6;\n}\n.tab-switcher .tabs .tab-wrapper .tab.active {\n background: transparent;\n z-index: 5;\n}\n.tab-switcher .tabs .tab-wrapper .tab img {\n max-height: 26px;\n vertical-align: top;\n margin-top: -5px;\n}\n.tab-switcher .tabs .tab-wrapper:not(.active)::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 7;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}",".with-load-more-footer {\n padding: 10px;\n text-align: center;\n border-top: 1px solid;\n border-top-color: #222;\n border-top-color: var(--border, #222);\n}\n.with-load-more-footer .error {\n font-size: 14px;\n}",".with-subscription-loading {\n padding: 10px;\n text-align: center;\n}\n.with-subscription-loading .error {\n font-size: 14px;\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/priv/static/static/font/config.json b/priv/static/static/font/config.json index 72a48a74f..c0cf17271 100755 --- a/priv/static/static/font/config.json +++ b/priv/static/static/font/config.json @@ -240,6 +240,12 @@ "code": 59419, "src": "fontawesome" }, + { + "uid": "d862a10e1448589215be19702f98f2c1", + "css": "smile", + "code": 61720, + "src": "fontawesome" + }, { "uid": "671f29fa10dda08074a4c6a341bb4f39", "css": "bell-alt", @@ -291,6 +297,12 @@ "css": "zoom-in", "code": 59420, "src": "fontawesome" + }, + { + "uid": "0bda4bc779d4c32623dec2e43bd67ee8", + "css": "gauge", + "code": 61668, + "src": "fontawesome" } ] } \ No newline at end of file diff --git a/priv/static/static/font/css/fontello-codes.css b/priv/static/static/font/css/fontello-codes.css index 2083f618a..87b4930e5 100755 Binary files a/priv/static/static/font/css/fontello-codes.css and b/priv/static/static/font/css/fontello-codes.css differ diff --git a/priv/static/static/font/css/fontello-embedded.css b/priv/static/static/font/css/fontello-embedded.css index ad4246e6e..861ef86e0 100755 Binary files a/priv/static/static/font/css/fontello-embedded.css and b/priv/static/static/font/css/fontello-embedded.css differ diff --git a/priv/static/static/font/css/fontello-ie7-codes.css b/priv/static/static/font/css/fontello-ie7-codes.css index cbc410004..11c8c10a2 100755 Binary files a/priv/static/static/font/css/fontello-ie7-codes.css and b/priv/static/static/font/css/fontello-ie7-codes.css differ diff --git a/priv/static/static/font/css/fontello-ie7.css b/priv/static/static/font/css/fontello-ie7.css index 1ef174bf8..edf83afe6 100755 Binary files a/priv/static/static/font/css/fontello-ie7.css and b/priv/static/static/font/css/fontello-ie7.css differ diff --git a/priv/static/static/font/css/fontello.css b/priv/static/static/font/css/fontello.css index 84fd6802c..a6b3c9193 100755 Binary files a/priv/static/static/font/css/fontello.css and b/priv/static/static/font/css/fontello.css differ diff --git a/priv/static/static/font/demo.html b/priv/static/static/font/demo.html index 225e4ec5b..afae72fa5 100755 --- a/priv/static/static/font/demo.html +++ b/priv/static/static/font/demo.html @@ -229,11 +229,11 @@ body { } @font-face { font-family: 'fontello'; - src: url('./font/fontello.eot?25455785'); - src: url('./font/fontello.eot?25455785#iefix') format('embedded-opentype'), - url('./font/fontello.woff?25455785') format('woff'), - url('./font/fontello.ttf?25455785') format('truetype'), - url('./font/fontello.svg?25455785#fontello') format('svg'); + src: url('./font/fontello.eot?56851497'); + src: url('./font/fontello.eot?56851497#iefix') format('embedded-opentype'), + url('./font/fontello.woff?56851497') format('woff'), + url('./font/fontello.ttf?56851497') format('truetype'), + url('./font/fontello.svg?56851497#fontello') format('svg'); font-weight: normal; font-style: normal; } @@ -349,21 +349,23 @@ body {
icon-link-ext-alt0xf08f
icon-menu0xf0c9
icon-mail-alt0xf0e0
-
icon-comment-empty0xf0e5
+
icon-gauge0xf0e4
+
icon-comment-empty0xf0e5
icon-bell-alt0xf0f3
icon-plus-squared0xf0fe
icon-reply0xf112
-
icon-lock-open-alt0xf13e
+
icon-smile0xf118
+
icon-lock-open-alt0xf13e
icon-ellipsis0xf141
icon-play-circled0xf144
-
icon-thumbs-up-alt0xf164
-
icon-binoculars0xf1e5
+
icon-thumbs-up-alt0xf164
+
icon-binoculars0xf1e5
icon-user-plus0xf234
diff --git a/priv/static/static/font/font/fontello.eot b/priv/static/static/font/font/fontello.eot index d08692e84..1703fd97f 100755 Binary files a/priv/static/static/font/font/fontello.eot and b/priv/static/static/font/font/fontello.eot differ diff --git a/priv/static/static/font/font/fontello.svg b/priv/static/static/font/font/fontello.svg index fdd7caa73..f5e497ce4 100755 --- a/priv/static/static/font/font/fontello.svg +++ b/priv/static/static/font/font/fontello.svg @@ -76,6 +76,8 @@ + + @@ -84,6 +86,8 @@ + + diff --git a/priv/static/static/font/font/fontello.ttf b/priv/static/static/font/font/fontello.ttf index 6f5a81d76..e9ed78031 100755 Binary files a/priv/static/static/font/font/fontello.ttf and b/priv/static/static/font/font/fontello.ttf differ diff --git a/priv/static/static/font/font/fontello.woff b/priv/static/static/font/font/fontello.woff index 79972a576..1d5025d3c 100755 Binary files a/priv/static/static/font/font/fontello.woff and b/priv/static/static/font/font/fontello.woff differ diff --git a/priv/static/static/font/font/fontello.woff2 b/priv/static/static/font/font/fontello.woff2 index 94d79274a..078991eb8 100755 Binary files a/priv/static/static/font/font/fontello.woff2 and b/priv/static/static/font/font/fontello.woff2 differ diff --git a/priv/static/static/js/2.48f39bc510b7c0a7fca6.js b/priv/static/static/js/2.48f39bc510b7c0a7fca6.js new file mode 100644 index 000000000..eabbcf690 Binary files /dev/null and b/priv/static/static/js/2.48f39bc510b7c0a7fca6.js differ diff --git a/priv/static/static/js/2.48f39bc510b7c0a7fca6.js.map b/priv/static/static/js/2.48f39bc510b7c0a7fca6.js.map new file mode 100644 index 000000000..be87ffa46 Binary files /dev/null and b/priv/static/static/js/2.48f39bc510b7c0a7fca6.js.map differ diff --git a/priv/static/static/js/app.4ab7097a5650339b9e3d.js b/priv/static/static/js/app.4ab7097a5650339b9e3d.js new file mode 100644 index 000000000..33141e412 Binary files /dev/null and b/priv/static/static/js/app.4ab7097a5650339b9e3d.js differ diff --git a/priv/static/static/js/app.4ab7097a5650339b9e3d.js.map b/priv/static/static/js/app.4ab7097a5650339b9e3d.js.map new file mode 100644 index 000000000..b47e90c09 Binary files /dev/null and b/priv/static/static/js/app.4ab7097a5650339b9e3d.js.map differ diff --git a/priv/static/static/js/vendors~app.c3a8e45fed39bd98810d.js b/priv/static/static/js/vendors~app.24e6ba2d196f6210feda.js similarity index 53% rename from priv/static/static/js/vendors~app.c3a8e45fed39bd98810d.js rename to priv/static/static/js/vendors~app.24e6ba2d196f6210feda.js index e0398e982..a8e3a2c04 100644 Binary files a/priv/static/static/js/vendors~app.c3a8e45fed39bd98810d.js and b/priv/static/static/js/vendors~app.24e6ba2d196f6210feda.js differ diff --git a/priv/static/static/js/vendors~app.24e6ba2d196f6210feda.js.map b/priv/static/static/js/vendors~app.24e6ba2d196f6210feda.js.map new file mode 100644 index 000000000..93d3ee58c Binary files /dev/null and b/priv/static/static/js/vendors~app.24e6ba2d196f6210feda.js.map differ diff --git a/priv/static/sw-pleroma.js b/priv/static/sw-pleroma.js index e9627ed00..679ff220f 100644 Binary files a/priv/static/sw-pleroma.js and b/priv/static/sw-pleroma.js differ diff --git a/test/activity_test.exs b/test/activity_test.exs index 95d9341c4..e7ea2bd5e 100644 --- a/test/activity_test.exs +++ b/test/activity_test.exs @@ -126,9 +126,25 @@ test "when association is not loaded" do } {:ok, local_activity} = Pleroma.Web.CommonAPI.post(user, %{"status" => "find me!"}) + {:ok, japanese_activity} = Pleroma.Web.CommonAPI.post(user, %{"status" => "更新情報"}) {:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params) {:ok, remote_activity} = ObanHelpers.perform(job) - %{local_activity: local_activity, remote_activity: remote_activity, user: user} + + %{ + japanese_activity: japanese_activity, + local_activity: local_activity, + remote_activity: remote_activity, + user: user + } + end + + test "finds utf8 text in statuses", %{ + japanese_activity: japanese_activity, + user: user + } do + activities = Activity.search(user, "更新情報") + + assert [^japanese_activity] = activities end test "find local and remote statuses for authenticated users", %{ diff --git a/test/conversation/participation_test.exs b/test/conversation/participation_test.exs index a27167d42..f430bdf75 100644 --- a/test/conversation/participation_test.exs +++ b/test/conversation/participation_test.exs @@ -6,6 +6,7 @@ defmodule Pleroma.Conversation.ParticipationTest do use Pleroma.DataCase import Pleroma.Factory alias Pleroma.Conversation.Participation + alias Pleroma.User alias Pleroma.Web.CommonAPI test "getting a participation will also preload things" do @@ -30,6 +31,8 @@ test "for a new conversation, it sets the recipents of the participation" do {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"}) + user = User.get_cached_by_id(user.id) + other_user = User.get_cached_by_id(user.id) [participation] = Participation.for_user(user) participation = Pleroma.Repo.preload(participation, :recipients) @@ -155,6 +158,7 @@ test "it sets recipients, always keeping the owner of the participation even whe [participation] = Participation.for_user_with_last_activity_id(user) participation = Repo.preload(participation, :recipients) + user = User.get_cached_by_id(user.id) assert participation.recipients |> length() == 1 assert user in participation.recipients diff --git a/test/emails/admin_email_test.exs b/test/emails/admin_email_test.exs index 31eac5f12..ad89f9213 100644 --- a/test/emails/admin_email_test.exs +++ b/test/emails/admin_email_test.exs @@ -19,8 +19,8 @@ test "build report email" do AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment") status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12") - reporter_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.nickname) - account_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, account.nickname) + reporter_url = Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id) + account_url = Helpers.feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id) assert res.to == [{to_user.name, to_user.email}] assert res.from == {config[:name], config[:notify_email]} diff --git a/test/fixtures/bogus-mastodon-announce.json b/test/fixtures/bogus-mastodon-announce.json new file mode 100644 index 000000000..0485b80b9 --- /dev/null +++ b/test/fixtures/bogus-mastodon-announce.json @@ -0,0 +1,43 @@ +{ + "type": "Announce", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "published": "2018-02-17T19:39:15Z", + "object": { + "type": "Note", + "id": "https://mastodon.social/users/emelie/statuses/101849165031453404", + "attributedTo": "https://mastodon.social/users/emelie", + "content": "this is a public toot", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://mastodon.social/users/emelie", + "https://mastodon.social/users/emelie/followers" + ] + }, + "id": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity", + "cc": [ + "http://mastodon.example.org/users/admin", + "http://mastodon.example.org/users/admin/followers" + ], + "atomUri": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity", + "actor": "http://mastodon.example.org/users/admin", + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "toot": "http://joinmastodon.org/ns#", + "sensitive": "as:sensitive", + "ostatus": "http://ostatus.org#", + "movedTo": "as:movedTo", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "atomUri": "ostatus:atomUri", + "Hashtag": "as:Hashtag", + "Emoji": "toot:Emoji" + } + ] +} diff --git a/test/fixtures/mastodon-announce-private.json b/test/fixtures/mastodon-announce-private.json new file mode 100644 index 000000000..9b868b13d --- /dev/null +++ b/test/fixtures/mastodon-announce-private.json @@ -0,0 +1,35 @@ +{ + "type": "Announce", + "to": [ + "http://mastodon.example.org/users/admin/followers" + ], + "published": "2018-02-17T19:39:15Z", + "object": { + "type": "Note", + "id": "http://mastodon.example.org/@admin/99541947525187368", + "attributedTo": "http://mastodon.example.org/users/admin", + "content": "this is a private toot", + "to": [ + "http://mastodon.example.org/users/admin/followers" + ] + }, + "id": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity", + "atomUri": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity", + "actor": "http://mastodon.example.org/users/admin", + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "toot": "http://joinmastodon.org/ns#", + "sensitive": "as:sensitive", + "ostatus": "http://ostatus.org#", + "movedTo": "as:movedTo", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "atomUri": "ostatus:atomUri", + "Hashtag": "as:Hashtag", + "Emoji": "toot:Emoji" + } + ] +} diff --git a/test/fixtures/tesla_mock/mstdn.jp_host_meta b/test/fixtures/tesla_mock/mstdn.jp_host_meta new file mode 100644 index 000000000..e76ddd47f --- /dev/null +++ b/test/fixtures/tesla_mock/mstdn.jp_host_meta @@ -0,0 +1,4 @@ + + + + diff --git a/test/fixtures/tesla_mock/pekorino@pawoo.net_host_meta.json b/test/fixtures/tesla_mock/pekorino@pawoo.net_host_meta.json new file mode 100644 index 000000000..3757c0dad --- /dev/null +++ b/test/fixtures/tesla_mock/pekorino@pawoo.net_host_meta.json @@ -0,0 +1,12 @@ +{ + "subject":"acct:pekorino@pawoo.net", + "aliases":["https://pawoo.net/@pekorino","https://pawoo.net/users/pekorino"], + "links":[ + {"rel":"http://webfinger.net/rel/profile-page","type":"text/html","href":"https://pawoo.net/@pekorino"}, + {"rel":"http://schemas.google.com/g/2010#updates-from","type":"application/atom+xml","href":"https://pawoo.net/users/pekorino.atom"}, + {"rel":"self","type":"application/activity+json","href":"https://pawoo.net/users/pekorino"}, + {"rel":"salmon","href":"https://pawoo.net/api/salmon/128378"}, + {"rel":"magic-public-key","href":"data:application/magic-public-key,RSA.1x8XXmBqzyb-QRkfUKxKPd7Ac2KbaFhdKy2FkJY64G-ifga-BppzEb62Q5TdkRdVKdHjh5qI7A1Hk3KfnNQcNWqqak-jxII_txC2grbWpp7v-boceD2pnzdVK5l-RR-9wEwxcoCUeRWS1Ak6DStqE5tFQOAK4IIGQB-thSQGlU75KZ-2080fPA3Xc_ycH3_eB4YqawSxXrh6IeScMevN0YHSF84GAcvhXmwLKZRugiF6nYrknbPEe_niIOmN8hhEXLN9_4kDcH83hkVZd5VXssRrxqDhtokx9emvTHkA7sY1AjYeehTPZErlV74GN-kFYLeI6DluXoSI2sX1QcS08w==.AQAB"}, + {"rel":"http://ostatus.org/schema/1.0/subscribe","template":"https://pawoo.net/authorize_follow?acct={uri}"} + ] +} diff --git a/test/fixtures/tesla_mock/sdf.org_host_meta b/test/fixtures/tesla_mock/sdf.org_host_meta new file mode 100644 index 000000000..0ffc4f096 --- /dev/null +++ b/test/fixtures/tesla_mock/sdf.org_host_meta @@ -0,0 +1,4 @@ + + + + diff --git a/test/fixtures/tesla_mock/snowdusk@sdf.org_host_meta.json b/test/fixtures/tesla_mock/snowdusk@sdf.org_host_meta.json new file mode 100644 index 000000000..273fc3804 --- /dev/null +++ b/test/fixtures/tesla_mock/snowdusk@sdf.org_host_meta.json @@ -0,0 +1,12 @@ +{ + "subject":"acct:snowdusk@mastodon.sdf.org", + "aliases":["https://mastodon.sdf.org/@snowdusk","https://mastodon.sdf.org/users/snowdusk"], + "links":[ + {"rel":"http://webfinger.net/rel/profile-page","type":"text/html","href":"https://mastodon.sdf.org/@snowdusk"}, + {"rel":"http://schemas.google.com/g/2010#updates-from","type":"application/atom+xml","href":"https://mastodon.sdf.org/users/snowdusk.atom"}, + {"rel":"self","type":"application/activity+json","href":"https://mastodon.sdf.org/users/snowdusk"}, + {"rel":"salmon","href":"https://mastodon.sdf.org/api/salmon/2"}, + {"rel":"magic-public-key","href":"data:application/magic-public-key,RSA.k4_Hr0WQUHumAD4uwWIz7OybovIKgIuanbXhX5pl7oGyb2TuifBf3nAqEhD6eLSo6-_6160L4BvPPV_l_6rlZEi6_nbeJUgVkayZgcZN3oou3IErSt8L0IbUdWT5s4fWM2zpkndLCkVbeeNQ3DOBccvJw7iA_QNTao8wr3ILvQaKEDnf-H5QBd9Tj3seyo4-7E0e6wCKOH_uBm8pSRgpdMdl2CehiFzaABBkmCeUKH-buU7iNQGi0fsV5VIHn6zffrv6p0EVNkjTDi1vTmmfrp9W0mcKZJ9DtvdehOKSgh3J7Mem-ILbPy6FSL2Oi6Ekj_Wh4M8Ie-YKuxI3N_0Baw==.AQAB"}, + {"rel":"http://ostatus.org/schema/1.0/subscribe","template":"https://mastodon.sdf.org/authorize_interaction?uri={uri}"} + ] +} diff --git a/test/fixtures/tesla_mock/soykaf.com_host_meta b/test/fixtures/tesla_mock/soykaf.com_host_meta new file mode 100644 index 000000000..99d552d32 --- /dev/null +++ b/test/fixtures/tesla_mock/soykaf.com_host_meta @@ -0,0 +1,4 @@ + + + + diff --git a/test/fixtures/tesla_mock/stopwatchingus-heidelberg.de_host_meta b/test/fixtures/tesla_mock/stopwatchingus-heidelberg.de_host_meta new file mode 100644 index 000000000..481cfec8d --- /dev/null +++ b/test/fixtures/tesla_mock/stopwatchingus-heidelberg.de_host_meta @@ -0,0 +1,31 @@ +{ + "links":[ + { + "rel":"lrdd", + "type":"application\/jrd+json", + "template":"https:\/\/social.stopwatchingus-heidelberg.de\/.well-known\/webfinger?resource={uri}" + }, + { + "rel":"lrdd", + "type":"application\/json", + "template":"https:\/\/social.stopwatchingus-heidelberg.de\/.well-known\/webfinger?resource={uri}" + }, + { + "rel":"lrdd", + "type":"application\/xrd+xml", + "template":"https:\/\/social.stopwatchingus-heidelberg.de\/.well-known\/webfinger?resource={uri}" + }, + { + "rel":"http:\/\/apinamespace.org\/oauth\/access_token", + "href":"https:\/\/social.stopwatchingus-heidelberg.de\/api\/oauth\/access_token" + }, + { + "rel":"http:\/\/apinamespace.org\/oauth\/request_token", + "href":"https:\/\/social.stopwatchingus-heidelberg.de\/api\/oauth\/request_token" + }, + { + "rel":"http:\/\/apinamespace.org\/oauth\/authorize", + "href":"https:\/\/social.stopwatchingus-heidelberg.de\/api\/oauth\/authorize" + } + ] +} diff --git a/test/fixtures/tesla_mock/xn--q9jyb4c_host_meta b/test/fixtures/tesla_mock/xn--q9jyb4c_host_meta new file mode 100644 index 000000000..45d260e55 --- /dev/null +++ b/test/fixtures/tesla_mock/xn--q9jyb4c_host_meta @@ -0,0 +1,4 @@ + + + + diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/plugs/oauth_scopes_plug_test.exs index 6a13ea811..be6d1340b 100644 --- a/test/plugs/oauth_scopes_plug_test.exs +++ b/test/plugs/oauth_scopes_plug_test.exs @@ -5,24 +5,48 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do use Pleroma.Web.ConnCase, async: true + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo + import Mock import Pleroma.Factory - test "proceeds with no op if `assigns[:token]` is nil", %{conn: conn} do - conn = - conn - |> assign(:user, insert(:user)) - |> OAuthScopesPlug.call(%{scopes: ["read"]}) - - refute conn.halted - assert conn.assigns[:user] + setup_with_mocks([{EnsurePublicOrAuthenticatedPlug, [], [call: fn conn, _ -> conn end]}]) do + :ok end - test "proceeds with no op if `token.scopes` fulfill specified 'any of' conditions", %{ - conn: conn - } do + describe "when `assigns[:token]` is nil, " do + test "with :skip_instance_privacy_check option, proceeds with no op", %{conn: conn} do + conn = + conn + |> assign(:user, insert(:user)) + |> OAuthScopesPlug.call(%{scopes: ["read"], skip_instance_privacy_check: true}) + + refute conn.halted + assert conn.assigns[:user] + + refute called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end + + test "without :skip_instance_privacy_check option, calls EnsurePublicOrAuthenticatedPlug", %{ + conn: conn + } do + conn = + conn + |> assign(:user, insert(:user)) + |> OAuthScopesPlug.call(%{scopes: ["read"]}) + + refute conn.halted + assert conn.assigns[:user] + + assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end + end + + test "if `token.scopes` fulfills specified 'any of' conditions, " <> + "proceeds with no op", + %{conn: conn} do token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) conn = @@ -35,9 +59,9 @@ test "proceeds with no op if `token.scopes` fulfill specified 'any of' condition assert conn.assigns[:user] end - test "proceeds with no op if `token.scopes` fulfill specified 'all of' conditions", %{ - conn: conn - } do + test "if `token.scopes` fulfills specified 'all of' conditions, " <> + "proceeds with no op", + %{conn: conn} do token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user) conn = @@ -50,73 +74,154 @@ test "proceeds with no op if `token.scopes` fulfill specified 'all of' condition assert conn.assigns[:user] end - test "proceeds with cleared `assigns[:user]` if `token.scopes` doesn't fulfill specified 'any of' conditions " <> - "and `fallback: :proceed_unauthenticated` option is specified", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + describe "with `fallback: :proceed_unauthenticated` option, " do + test "if `token.scopes` doesn't fulfill specified 'any of' conditions, " <> + "clears `assigns[:user]` and calls EnsurePublicOrAuthenticatedPlug", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: ["follow"], fallback: :proceed_unauthenticated}) + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["follow"], fallback: :proceed_unauthenticated}) - refute conn.halted - refute conn.assigns[:user] + refute conn.halted + refute conn.assigns[:user] + + assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end + + test "if `token.scopes` doesn't fulfill specified 'all of' conditions, " <> + "clears `assigns[:user] and calls EnsurePublicOrAuthenticatedPlug", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{ + scopes: ["read", "follow"], + op: :&, + fallback: :proceed_unauthenticated + }) + + refute conn.halted + refute conn.assigns[:user] + + assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end + + test "with :skip_instance_privacy_check option, " <> + "if `token.scopes` doesn't fulfill specified conditions, " <> + "clears `assigns[:user]` and does not call EnsurePublicOrAuthenticatedPlug", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read:statuses", "write"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{ + scopes: ["read"], + fallback: :proceed_unauthenticated, + skip_instance_privacy_check: true + }) + + refute conn.halted + refute conn.assigns[:user] + + refute called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end end - test "proceeds with cleared `assigns[:user]` if `token.scopes` doesn't fulfill specified 'all of' conditions " <> - "and `fallback: :proceed_unauthenticated` option is specified", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + describe "without :fallback option, " do + test "if `token.scopes` does not fulfill specified 'any of' conditions, " <> + "returns 403 and halts", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) + any_of_scopes = ["follow"] - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{ - scopes: ["read", "follow"], - op: :&, - fallback: :proceed_unauthenticated - }) + conn = + conn + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: any_of_scopes}) - refute conn.halted - refute conn.assigns[:user] + assert conn.halted + assert 403 == conn.status + + expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, ", ")}." + assert Jason.encode!(%{error: expected_error}) == conn.resp_body + end + + test "if `token.scopes` does not fulfill specified 'all of' conditions, " <> + "returns 403 and halts", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) + all_of_scopes = ["write", "follow"] + + conn = + conn + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&}) + + assert conn.halted + assert 403 == conn.status + + expected_error = + "Insufficient permissions: #{Enum.join(all_of_scopes -- token.scopes, ", ")}." + + assert Jason.encode!(%{error: expected_error}) == conn.resp_body + end end - test "returns 403 and halts in case of no :fallback option and `token.scopes` not fulfilling specified 'any of' conditions", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) - any_of_scopes = ["follow"] + describe "with hierarchical scopes, " do + test "if `token.scopes` fulfills specified 'any of' conditions, " <> + "proceeds with no op", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) - conn = - conn - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: any_of_scopes}) + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["read:something"]}) - assert conn.halted - assert 403 == conn.status + refute conn.halted + assert conn.assigns[:user] + end - expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, ", ")}." - assert Jason.encode!(%{error: expected_error}) == conn.resp_body + test "if `token.scopes` fulfills specified 'all of' conditions, " <> + "proceeds with no op", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["scope1:subscope", "scope2:subscope"], op: :&}) + + refute conn.halted + assert conn.assigns[:user] + end end - test "returns 403 and halts in case of no :fallback option and `token.scopes` not fulfilling specified 'all of' conditions", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) - all_of_scopes = ["write", "follow"] + describe "filter_descendants/2" do + test "filters scopes which directly match or are ancestors of supported scopes" do + f = fn scopes, supported_scopes -> + OAuthScopesPlug.filter_descendants(scopes, supported_scopes) + end - conn = - conn - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&}) + assert f.(["read", "follow"], ["write", "read"]) == ["read"] - assert conn.halted - assert 403 == conn.status + assert f.(["read", "write:something", "follow"], ["write", "read"]) == + ["read", "write:something"] - expected_error = - "Insufficient permissions: #{Enum.join(all_of_scopes -- token.scopes, ", ")}." + assert f.(["admin:read"], ["write", "read"]) == [] - assert Jason.encode!(%{error: expected_error}) == conn.resp_body + assert f.(["admin:read"], ["write", "admin"]) == ["admin:read"] + end end end diff --git a/test/signature_test.exs b/test/signature_test.exs index d5bf63d7d..96c8ba07a 100644 --- a/test/signature_test.exs +++ b/test/signature_test.exs @@ -80,7 +80,7 @@ test "it returns signature headers" do user = insert(:user, %{ ap_id: "https://mastodon.social/users/lambadalambda", - info: %{keys: @private_key} + keys: @private_key }) assert Signature.sign( @@ -94,8 +94,7 @@ test "it returns signature headers" do end test "it returns error" do - user = - insert(:user, %{ap_id: "https://mastodon.social/users/lambadalambda", info: %{keys: ""}}) + user = insert(:user, %{ap_id: "https://mastodon.social/users/lambadalambda", keys: ""}) assert Signature.sign( user, diff --git a/test/support/factory.ex b/test/support/factory.ex index 4f3244025..b180844cd 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -324,6 +324,7 @@ def oauth_token_factory do %Pleroma.Web.OAuth.Token{ token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(), + scopes: ["read"], refresh_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(), user: build(:user), app_id: oauth_app.id, diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index 5506c0626..4feb57f3a 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -46,6 +46,14 @@ def get("https://mastodon.social/users/emelie/statuses/101849165031453009", _, _ }} end + def get("https://mastodon.social/users/emelie/statuses/101849165031453404", _, _, _) do + {:ok, + %Tesla.Env{ + status: 404, + body: "" + }} + end + def get("https://mastodon.social/users/emelie", _, _, _) do {:ok, %Tesla.Env{ @@ -336,6 +344,181 @@ def get("http://mastodon.example.org/users/gargron", _, _, Accept: "application/ {:error, :nxdomain} end + def get("http://osada.macgirvin.com/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 404, + body: "" + }} + end + + def get("https://osada.macgirvin.com/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 404, + body: "" + }} + end + + def get("http://mastodon.sdf.org/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/sdf.org_host_meta") + }} + end + + def get("https://mastodon.sdf.org/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/sdf.org_host_meta") + }} + end + + def get( + "https://mastodon.sdf.org/.well-known/webfinger?resource=https://mastodon.sdf.org/users/snowdusk", + _, + _, + _ + ) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/snowdusk@sdf.org_host_meta.json") + }} + end + + def get("http://mstdn.jp/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mstdn.jp_host_meta") + }} + end + + def get("https://mstdn.jp/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mstdn.jp_host_meta") + }} + end + + def get("https://mstdn.jp/.well-known/webfinger?resource=kpherox@mstdn.jp", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/kpherox@mstdn.jp.xml") + }} + end + + def get("http://mamot.fr/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mamot.fr_host_meta") + }} + end + + def get("https://mamot.fr/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mamot.fr_host_meta") + }} + end + + def get( + "https://mamot.fr/.well-known/webfinger?resource=https://mamot.fr/users/Skruyb", + _, + _, + _ + ) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/skruyb@mamot.fr.atom") + }} + end + + def get("http://pawoo.net/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/pawoo.net_host_meta") + }} + end + + def get("https://pawoo.net/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/pawoo.net_host_meta") + }} + end + + def get( + "https://pawoo.net/.well-known/webfinger?resource=https://pawoo.net/users/pekorino", + _, + _, + _ + ) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/pekorino@pawoo.net_host_meta.json") + }} + end + + def get("http://zetsubou.xn--q9jyb4c/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/xn--q9jyb4c_host_meta") + }} + end + + def get("https://zetsubou.xn--q9jyb4c/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/xn--q9jyb4c_host_meta") + }} + end + + def get("http://pleroma.soykaf.com/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/soykaf.com_host_meta") + }} + end + + def get("https://pleroma.soykaf.com/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/soykaf.com_host_meta") + }} + end + + def get("http://social.stopwatchingus-heidelberg.de/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/stopwatchingus-heidelberg.de_host_meta") + }} + end + + def get("https://social.stopwatchingus-heidelberg.de/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/stopwatchingus-heidelberg.de_host_meta") + }} + end + def get( "http://mastodon.example.org/@admin/99541947525187367", _, @@ -349,6 +532,14 @@ def get( }} end + def get("http://mastodon.example.org/@admin/99541947525187368", _, _, _) do + {:ok, + %Tesla.Env{ + status: 404, + body: "" + }} + end + def get("https://shitposter.club/notice/7369654", _, _, _) do {:ok, %Tesla.Env{ diff --git a/test/tasks/count_statuses_test.exs b/test/tasks/count_statuses_test.exs new file mode 100644 index 000000000..6035da3c3 --- /dev/null +++ b/test/tasks/count_statuses_test.exs @@ -0,0 +1,39 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.CountStatusesTest do + use Pleroma.DataCase + + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import ExUnit.CaptureIO, only: [capture_io: 1] + import Pleroma.Factory + + test "counts statuses" do + user = insert(:user) + {:ok, _} = CommonAPI.post(user, %{"status" => "test"}) + {:ok, _} = CommonAPI.post(user, %{"status" => "test2"}) + + user2 = insert(:user) + {:ok, _} = CommonAPI.post(user2, %{"status" => "test3"}) + + user = refresh_record(user) + user2 = refresh_record(user2) + + assert %{info: %{note_count: 2}} = user + assert %{info: %{note_count: 1}} = user2 + + {:ok, user} = User.update_info(user, &User.Info.set_note_count(&1, 0)) + {:ok, user2} = User.update_info(user2, &User.Info.set_note_count(&1, 0)) + + assert %{info: %{note_count: 0}} = user + assert %{info: %{note_count: 0}} = user2 + + assert capture_io(fn -> Mix.Tasks.Pleroma.CountStatuses.run([]) end) == "Done\n" + + assert %{info: %{note_count: 2}} = refresh_record(user) + assert %{info: %{note_count: 1}} = refresh_record(user2) + end +end diff --git a/test/user_test.exs b/test/user_test.exs index 1bc853c94..019e7b400 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -515,7 +515,7 @@ test "returns an ap_id for a user" do user = insert(:user) assert User.ap_id(user) == - Pleroma.Web.Router.Helpers.o_status_url( + Pleroma.Web.Router.Helpers.feed_url( Pleroma.Web.Endpoint, :feed_redirect, user.nickname @@ -526,7 +526,7 @@ test "returns an ap_followers link for a user" do user = insert(:user) assert User.ap_followers(user) == - Pleroma.Web.Router.Helpers.o_status_url( + Pleroma.Web.Router.Helpers.feed_url( Pleroma.Web.Endpoint, :feed_redirect, user.nickname @@ -1457,15 +1457,15 @@ test "if user is unconfirmed" do describe "ensure_keys_present" do test "it creates keys for a user and stores them in info" do user = insert(:user) - refute is_binary(user.info.keys) + refute is_binary(user.keys) {:ok, user} = User.ensure_keys_present(user) - assert is_binary(user.info.keys) + assert is_binary(user.keys) end test "it doesn't create keys if there already are some" do - user = insert(:user, %{info: %{keys: "xxx"}}) + user = insert(:user, keys: "xxx") {:ok, user} = User.ensure_keys_present(user) - assert user.info.keys == "xxx" + assert user.keys == "xxx" end end diff --git a/test/web/activity_pub/mrf/simple_policy_test.exs b/test/web/activity_pub/mrf/simple_policy_test.exs index 7203b27da..df0f223f8 100644 --- a/test/web/activity_pub/mrf/simple_policy_test.exs +++ b/test/web/activity_pub/mrf/simple_policy_test.exs @@ -236,7 +236,7 @@ test "is empty" do assert SimplePolicy.filter(remote_message) == {:ok, remote_message} end - test "has a matching host" do + test "activity has a matching host" do Config.put([:mrf_simple, :reject], ["remote.instance"]) remote_message = build_remote_message() @@ -244,13 +244,21 @@ test "has a matching host" do assert SimplePolicy.filter(remote_message) == {:reject, nil} end - test "match with wildcard domain" do + test "activity matches with wildcard domain" do Config.put([:mrf_simple, :reject], ["*.remote.instance"]) remote_message = build_remote_message() assert SimplePolicy.filter(remote_message) == {:reject, nil} end + + test "actor has a matching host" do + Config.put([:mrf_simple, :reject], ["remote.instance"]) + + remote_user = build_remote_user() + + assert SimplePolicy.filter(remote_user) == {:reject, nil} + end end describe "when :accept" do @@ -264,7 +272,7 @@ test "is empty" do assert SimplePolicy.filter(remote_message) == {:ok, remote_message} end - test "is not empty but it doesn't have a matching host" do + test "is not empty but activity doesn't have a matching host" do Config.put([:mrf_simple, :accept], ["non.matching.remote"]) local_message = build_local_message() @@ -274,7 +282,7 @@ test "is not empty but it doesn't have a matching host" do assert SimplePolicy.filter(remote_message) == {:reject, nil} end - test "has a matching host" do + test "activity has a matching host" do Config.put([:mrf_simple, :accept], ["remote.instance"]) local_message = build_local_message() @@ -284,7 +292,7 @@ test "has a matching host" do assert SimplePolicy.filter(remote_message) == {:ok, remote_message} end - test "match with wildcard domain" do + test "activity matches with wildcard domain" do Config.put([:mrf_simple, :accept], ["*.remote.instance"]) local_message = build_local_message() @@ -293,6 +301,14 @@ test "match with wildcard domain" do assert SimplePolicy.filter(local_message) == {:ok, local_message} assert SimplePolicy.filter(remote_message) == {:ok, remote_message} end + + test "actor has a matching host" do + Config.put([:mrf_simple, :accept], ["remote.instance"]) + + remote_user = build_remote_user() + + assert SimplePolicy.filter(remote_user) == {:ok, remote_user} + end end describe "when :avatar_removal" do diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 475313316..50c0bfb84 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -442,6 +442,33 @@ test "it works for incoming announces with an existing activity" do assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id end + test "it works for incoming announces with an inlined activity" do + data = + File.read!("test/fixtures/mastodon-announce-private.json") + |> Poison.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Announce" + + assert data["id"] == + "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + + object = Object.normalize(data["object"]) + + assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368" + assert object.data["content"] == "this is a private toot" + end + + test "it rejects incoming announces with an inlined activity from another origin" do + data = + File.read!("test/fixtures/bogus-mastodon-announce.json") + |> Poison.decode!() + + assert :error = Transmogrifier.handle_incoming(data) + end + test "it does not clobber the addressing on announce activities" do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) diff --git a/test/web/feed/feed_controller_test.exs b/test/web/feed/feed_controller_test.exs new file mode 100644 index 000000000..1f44eae20 --- /dev/null +++ b/test/web/feed/feed_controller_test.exs @@ -0,0 +1,227 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Feed.FeedControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + alias Pleroma.Object + alias Pleroma.User + + test "gets a feed", %{conn: conn} do + activity = insert(:note_activity) + + note = + insert(:note, + data: %{ + "attachment" => [ + %{ + "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"}] + } + ], + "inReplyTo" => activity.data["id"] + } + ) + + note_activity = insert(:note_activity, note: note) + object = Object.normalize(note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + conn = + conn + |> put_req_header("content-type", "application/atom+xml") + |> get("/users/#{user.nickname}/feed.atom") + + assert response(conn, 200) =~ object.data["content"] + end + + test "returns 404 for a missing feed", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/atom+xml") + |> get("/users/nonexisting/feed.atom") + + assert response(conn, 404) + end + + describe "feed_redirect" do + test "undefined format. it redirects to feed", %{conn: conn} do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + response = + conn + |> put_req_header("accept", "application/xml") + |> get("/users/#{user.nickname}") + |> response(302) + + assert response == + "You are being redirected." + end + + test "undefined format. it returns error when user not found", %{conn: conn} do + response = + conn + |> put_req_header("accept", "application/xml") + |> get("/users/jimm") + |> response(404) + + assert response == ~S({"error":"Not found"}) + end + + test "activity+json format. it redirects on actual feed of user", %{conn: conn} do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + response = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/users/#{user.nickname}") + |> json_response(200) + + assert response["endpoints"] == %{ + "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize", + "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps", + "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token", + "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox", + "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media" + } + + assert response["@context"] == [ + "https://www.w3.org/ns/activitystreams", + "http://localhost:4001/schemas/litepub-0.1.jsonld", + %{"@language" => "und"} + ] + + assert Map.take(response, [ + "followers", + "following", + "id", + "inbox", + "manuallyApprovesFollowers", + "name", + "outbox", + "preferredUsername", + "summary", + "tag", + "type", + "url" + ]) == %{ + "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers", + "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following", + "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}", + "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox", + "manuallyApprovesFollowers" => false, + "name" => user.name, + "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox", + "preferredUsername" => user.nickname, + "summary" => user.bio, + "tag" => [], + "type" => "Person", + "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}" + } + end + + test "activity+json format. it returns error whe use not found", %{conn: conn} do + response = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/users/jimm") + |> json_response(404) + + assert response == "Not found" + end + + test "json format. it redirects on actual feed of user", %{conn: conn} do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + response = + conn + |> put_req_header("accept", "application/json") + |> get("/users/#{user.nickname}") + |> json_response(200) + + assert response["endpoints"] == %{ + "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize", + "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps", + "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token", + "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox", + "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media" + } + + assert response["@context"] == [ + "https://www.w3.org/ns/activitystreams", + "http://localhost:4001/schemas/litepub-0.1.jsonld", + %{"@language" => "und"} + ] + + assert Map.take(response, [ + "followers", + "following", + "id", + "inbox", + "manuallyApprovesFollowers", + "name", + "outbox", + "preferredUsername", + "summary", + "tag", + "type", + "url" + ]) == %{ + "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers", + "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following", + "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}", + "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox", + "manuallyApprovesFollowers" => false, + "name" => user.name, + "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox", + "preferredUsername" => user.nickname, + "summary" => user.bio, + "tag" => [], + "type" => "Person", + "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}" + } + end + + test "json format. it returns error whe use not found", %{conn: conn} do + response = + conn + |> put_req_header("accept", "application/json") + |> get("/users/jimm") + |> json_response(404) + + assert response == "Not found" + end + + test "html format. it redirects on actual feed of user", %{conn: conn} do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + response = + conn + |> get("/users/#{user.nickname}") + |> response(200) + + assert response == + Fallback.RedirectController.redirector_with_meta( + conn, + %{user: user} + ).resp_body + end + + test "html format. it returns error when user not found", %{conn: conn} do + response = + conn + |> get("/users/jimm") + |> json_response(404) + + assert response == %{"error" => "Not found"} + end + end +end diff --git a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs index 560f55137..618031b40 100644 --- a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs @@ -272,7 +272,7 @@ test "updates the user's background", %{conn: conn} do assert user_response["pleroma"]["background_image"] end - test "requires 'write' permission", %{conn: conn} do + test "requires 'write:accounts' permission", %{conn: conn} do token1 = insert(:oauth_token, scopes: ["read"]) token2 = insert(:oauth_token, scopes: ["write", "follow"]) @@ -283,7 +283,8 @@ test "requires 'write' permission", %{conn: conn} do |> patch("/api/v1/accounts/update_credentials", %{}) if token == token1 do - assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403) + assert %{"error" => "Insufficient permissions: write:accounts."} == + json_response(conn, 403) else assert json_response(conn, 200) end @@ -328,7 +329,7 @@ test "update fields", %{conn: conn} do account = conn |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"fields" => fields}) + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(200) assert account["fields"] == [ @@ -344,6 +345,35 @@ test "update fields", %{conn: conn} do %{"name" => "link", "value" => "cofe.io"} ] + fields = + [ + "fields_attributes[1][name]=link", + "fields_attributes[1][value]=cofe.io", + "fields_attributes[0][name]=foo", + "fields_attributes[0][value]=bar" + ] + |> Enum.join("&") + + account = + conn + |> put_req_header("content-type", "application/x-www-form-urlencoded") + |> assign(:user, user) + |> patch("/api/v1/accounts/update_credentials", fields) + |> json_response(200) + + assert account["fields"] == [ + %{"name" => "foo", "value" => "bar"}, + %{"name" => "link", "value" => ~S(cofe.io)} + ] + + assert account["source"]["fields"] == [ + %{ + "name" => "foo", + "value" => "bar" + }, + %{"name" => "link", "value" => "cofe.io"} + ] + name_limit = Pleroma.Config.get([:instance, :account_field_name_length]) value_limit = Pleroma.Config.get([:instance, :account_field_value_length]) @@ -354,7 +384,7 @@ test "update fields", %{conn: conn} do assert %{"error" => "Invalid request"} == conn |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"fields" => fields}) + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(403) long_name = Enum.map(0..name_limit, fn _ -> "x" end) |> Enum.join() @@ -364,7 +394,7 @@ test "update fields", %{conn: conn} do assert %{"error" => "Invalid request"} == conn |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"fields" => fields}) + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(403) Pleroma.Config.put([:instance, :max_account_fields], 1) @@ -377,8 +407,23 @@ test "update fields", %{conn: conn} do assert %{"error" => "Invalid request"} == conn |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"fields" => fields}) + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response(403) + + fields = [ + %{"name" => "foo", "value" => ""}, + %{"name" => "", "value" => "bar"} + ] + + account = + conn + |> assign(:user, user) + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) + |> json_response(200) + + assert account["fields"] == [ + %{"name" => "foo", "value" => ""} + ] end end end diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index 8c8017838..6a59c3d94 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -849,4 +849,34 @@ test "returns an empty list on a bad request", %{conn: conn} do assert [] = json_response(conn, 200) end end + + test "getting a list of mutes", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, user} = User.mute(user, other_user) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/mutes") + + other_user_id = to_string(other_user.id) + assert [%{"id" => ^other_user_id}] = json_response(conn, 200) + end + + test "getting a list of blocks", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, user} = User.block(user, other_user) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/blocks") + + other_user_id = to_string(other_user.id) + assert [%{"id" => ^other_user_id}] = json_response(conn, 200) + end end diff --git a/test/web/mastodon_api/controllers/conversation_controller_test.exs b/test/web/mastodon_api/controllers/conversation_controller_test.exs index 7117fc76a..a308a7620 100644 --- a/test/web/mastodon_api/controllers/conversation_controller_test.exs +++ b/test/web/mastodon_api/controllers/conversation_controller_test.exs @@ -10,19 +10,23 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do import Pleroma.Factory - test "Conversations", %{conn: conn} do + test "returns a list of conversations", %{conn: conn} do user_one = insert(:user) user_two = insert(:user) user_three = insert(:user) {:ok, user_two} = User.follow(user_two, user_one) + assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 0 + {:ok, direct} = CommonAPI.post(user_one, %{ "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!", "visibility" => "direct" }) + assert User.get_cached_by_id(user_two.id).info.unread_conversation_count == 1 + {:ok, _follower_only} = CommonAPI.post(user_one, %{ "status" => "Hi @#{user_two.nickname}!", @@ -52,23 +56,100 @@ test "Conversations", %{conn: conn} do assert is_binary(res_id) assert unread == true assert res_last_status["id"] == direct.id + assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1 + end + + test "updates the last_status on reply", %{conn: conn} do + user_one = insert(:user) + user_two = insert(:user) + + {:ok, direct} = + CommonAPI.post(user_one, %{ + "status" => "Hi @#{user_two.nickname}", + "visibility" => "direct" + }) + + {:ok, direct_reply} = + CommonAPI.post(user_two, %{ + "status" => "reply", + "visibility" => "direct", + "in_reply_to_status_id" => direct.id + }) + + [%{"last_status" => res_last_status}] = + conn + |> assign(:user, user_one) + |> get("/api/v1/conversations") + |> json_response(200) + + assert res_last_status["id"] == direct_reply.id + end + + test "the user marks a conversation as read", %{conn: conn} do + user_one = insert(:user) + user_two = insert(:user) + + {:ok, direct} = + CommonAPI.post(user_one, %{ + "status" => "Hi @#{user_two.nickname}", + "visibility" => "direct" + }) + + [%{"id" => direct_conversation_id, "unread" => true}] = + conn + |> assign(:user, user_one) + |> get("/api/v1/conversations") + |> json_response(200) + + %{"unread" => false} = + conn + |> assign(:user, user_one) + |> post("/api/v1/conversations/#{direct_conversation_id}/read") + |> json_response(200) + + assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 0 + + # The conversation is marked as unread on reply + {:ok, _} = + CommonAPI.post(user_two, %{ + "status" => "reply", + "visibility" => "direct", + "in_reply_to_status_id" => direct.id + }) + + [%{"unread" => true}] = + conn + |> assign(:user, user_one) + |> get("/api/v1/conversations") + |> json_response(200) + + assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1 + + # A reply doesn't increment the user's unread_conversation_count if the conversation is unread + {:ok, _} = + CommonAPI.post(user_two, %{ + "status" => "reply", + "visibility" => "direct", + "in_reply_to_status_id" => direct.id + }) + + assert User.get_cached_by_id(user_one.id).info.unread_conversation_count == 1 + end + + test "(vanilla) Mastodon frontend behaviour", %{conn: conn} do + user_one = insert(:user) + user_two = insert(:user) + + {:ok, direct} = + CommonAPI.post(user_one, %{ + "status" => "Hi @#{user_two.nickname}!", + "visibility" => "direct" + }) - # Apparently undocumented API endpoint res_conn = conn |> assign(:user, user_one) - |> post("/api/v1/conversations/#{res_id}/read") - - assert response = json_response(res_conn, 200) - assert length(response["accounts"]) == 2 - assert response["last_status"]["id"] == direct.id - assert response["unread"] == false - - # (vanilla) Mastodon frontend behaviour - res_conn = - conn - |> assign(:user, user_one) - |> get("/api/v1/statuses/#{res_last_status["id"]}/context") + |> get("/api/v1/statuses/#{direct.id}/context") assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200) end diff --git a/test/web/mastodon_api/controllers/search_controller_test.exs b/test/web/mastodon_api/controllers/search_controller_test.exs index 49c79ff0a..0ca896e01 100644 --- a/test/web/mastodon_api/controllers/search_controller_test.exs +++ b/test/web/mastodon_api/controllers/search_controller_test.exs @@ -40,7 +40,7 @@ test "it returns empty result if user or status search return undefined error", test "search", %{conn: conn} do user = insert(:user) user_two = insert(:user, %{nickname: "shp@shitposter.club"}) - user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) + user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu 天子"}) {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu private"}) @@ -52,9 +52,9 @@ test "search", %{conn: conn} do {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) - conn = get(conn, "/api/v2/search", %{"q" => "2hu #private"}) - - assert results = json_response(conn, 200) + results = + get(conn, "/api/v2/search", %{"q" => "2hu #private"}) + |> json_response(200) [account | _] = results["accounts"] assert account["id"] == to_string(user_three.id) @@ -65,6 +65,13 @@ test "search", %{conn: conn} do [status] = results["statuses"] assert status["id"] == to_string(activity.id) + + results = + get(conn, "/api/v2/search", %{"q" => "天子"}) + |> json_response(200) + + [account] == results["accounts"] + assert account["id"] == to_string(user_three.id) end end diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index b648ad6ff..2de2725e0 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do alias Pleroma.Activity alias Pleroma.ActivityExpiration alias Pleroma.Config + alias Pleroma.Conversation.Participation alias Pleroma.Object alias Pleroma.Repo alias Pleroma.ScheduledActivity @@ -465,6 +466,24 @@ test "get a status", %{conn: conn} do assert id == to_string(activity.id) end + test "get a direct status", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{"status" => "@#{other_user.nickname}", "visibility" => "direct"}) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/statuses/#{activity.id}") + + [participation] = Participation.for_user(user) + + res = json_response(conn, 200) + assert res["pleroma"]["direct_conversation_id"] == participation.id + end + test "get statuses by IDs", %{conn: conn} do %{id: id1} = insert(:note_activity) %{id: id2} = insert(:note_activity) @@ -1242,4 +1261,51 @@ test "context" do "descendants" => [%{"id" => ^id4}, %{"id" => ^id5}] } = response end + + test "returns the favorites of a user", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"}) + {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"}) + + {:ok, _, _} = CommonAPI.favorite(activity.id, user) + + first_conn = + conn + |> assign(:user, user) + |> get("/api/v1/favourites") + + assert [status] = json_response(first_conn, 200) + assert status["id"] == to_string(activity.id) + + assert [{"link", _link_header}] = + Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end) + + # Honours query params + {:ok, second_activity} = + CommonAPI.post(other_user, %{ + "status" => + "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful." + }) + + {:ok, _, _} = CommonAPI.favorite(second_activity.id, user) + + last_like = status["id"] + + second_conn = + conn + |> assign(:user, user) + |> get("/api/v1/favourites?since_id=#{last_like}") + + assert [second_status] = json_response(second_conn, 200) + assert second_status["id"] == to_string(second_activity.id) + + third_conn = + conn + |> assign(:user, user) + |> get("/api/v1/favourites?limit=0") + + assert [] = json_response(third_conn, 200) + 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 c03003dac..42a8779c0 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -7,7 +7,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do alias Pleroma.Notification alias Pleroma.Repo - alias Pleroma.User alias Pleroma.Web.CommonAPI import Pleroma.Factory @@ -20,36 +19,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do clear_config([:rich_media, :enabled]) - test "getting a list of mutes", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - {:ok, user} = User.mute(user, other_user) - - conn = - conn - |> assign(:user, user) - |> get("/api/v1/mutes") - - other_user_id = to_string(other_user.id) - assert [%{"id" => ^other_user_id}] = json_response(conn, 200) - end - - test "getting a list of blocks", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - {:ok, user} = User.block(user, other_user) - - conn = - conn - |> assign(:user, user) - |> get("/api/v1/blocks") - - other_user_id = to_string(other_user.id) - assert [%{"id" => ^other_user_id}] = json_response(conn, 200) - end - test "unimplemented follow_requests, blocks, domain blocks" do user = insert(:user) @@ -64,53 +33,6 @@ test "unimplemented follow_requests, blocks, domain blocks" do end) end - test "returns the favorites of a user", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"}) - {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"}) - - {:ok, _, _} = CommonAPI.favorite(activity.id, user) - - first_conn = - conn - |> assign(:user, user) - |> get("/api/v1/favourites") - - assert [status] = json_response(first_conn, 200) - assert status["id"] == to_string(activity.id) - - assert [{"link", _link_header}] = - Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end) - - # Honours query params - {:ok, second_activity} = - CommonAPI.post(other_user, %{ - "status" => - "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful." - }) - - {:ok, _, _} = CommonAPI.favorite(second_activity.id, user) - - last_like = status["id"] - - second_conn = - conn - |> assign(:user, user) - |> get("/api/v1/favourites?since_id=#{last_like}") - - assert [second_status] = json_response(second_conn, 200) - assert second_status["id"] == to_string(second_activity.id) - - third_conn = - conn - |> assign(:user, user) - |> get("/api/v1/favourites?limit=0") - - assert [] = json_response(third_conn, 200) - end - describe "link headers" do test "preserves parameters in link headers", %{conn: conn} do user = insert(:user) diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index 62b2ab7e3..b7a4938a6 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -418,6 +418,27 @@ test "shows actual follower/following count to the account owner" do following_count: 1 } = AccountView.render("show.json", %{user: user, for: user}) end + + test "shows unread_conversation_count only to the account owner" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _activity} = + CommonAPI.post(user, %{ + "status" => "Hey @#{other_user.nickname}.", + "visibility" => "direct" + }) + + user = User.get_cached_by_ap_id(user.ap_id) + + assert AccountView.render("show.json", %{user: user, for: other_user})[:pleroma][ + :unread_conversation_count + ] == nil + + assert AccountView.render("show.json", %{user: user, for: user})[:pleroma][ + :unread_conversation_count + ] == 1 + end end describe "follow requests counter" do diff --git a/test/web/metadata/feed_test.exs b/test/web/metadata/feed_test.exs new file mode 100644 index 000000000..50e9ce52e --- /dev/null +++ b/test/web/metadata/feed_test.exs @@ -0,0 +1,18 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Providers.FeedTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.Metadata.Providers.Feed + + test "it renders a link to user's atom feed" do + user = insert(:user, nickname: "lain") + + assert Feed.build_tags(%{user: user}) == [ + {:link, + [rel: "alternate", type: "application/atom+xml", href: "/users/lain/feed.atom"], []} + ] + end +end diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index 4d0741d14..41aaf6189 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -557,7 +557,7 @@ test "redirects with oauth authorization" do "password" => "test", "client_id" => app.client_id, "redirect_uri" => redirect_uri, - "scope" => "read write", + "scope" => "read:subscope write", "state" => "statepassed" } }) @@ -570,7 +570,7 @@ test "redirects with oauth authorization" do assert %{"state" => "statepassed", "code" => code} = query auth = Repo.get_by(Authorization, token: code) assert auth - assert auth.scopes == ["read", "write"] + assert auth.scopes == ["read:subscope", "write"] end test "returns 401 for wrong credentials", %{conn: conn} do @@ -627,7 +627,7 @@ test "returns 401 for missing scopes", %{conn: conn} do assert result =~ "This action is outside the authorized scopes" end - test "returns 401 for scopes beyond app scopes", %{conn: conn} do + test "returns 401 for scopes beyond app scopes hierarchy", %{conn: conn} do user = insert(:user) app = insert(:oauth_app, scopes: ["read", "write"]) redirect_uri = OAuthController.default_redirect_uri(app) diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs index f06023dff..b1af918d8 100644 --- a/test/web/ostatus/ostatus_controller_test.exs +++ b/test/web/ostatus/ostatus_controller_test.exs @@ -72,28 +72,6 @@ test "decodes a salmon with a changed magic key", %{conn: conn} do end end - test "gets a feed", %{conn: conn} do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - conn = - conn - |> put_req_header("content-type", "application/atom+xml") - |> get("/users/#{user.nickname}/feed.atom") - - assert response(conn, 200) =~ object.data["content"] - end - - test "returns 404 for a missing feed", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/atom+xml") - |> get("/users/nonexisting/feed.atom") - - assert response(conn, 404) - end - describe "GET object/2" do test "gets an object", %{conn: conn} do note_activity = insert(:note_activity) @@ -355,185 +333,6 @@ test "404s a nonexisting notice", %{conn: conn} do end end - describe "feed_redirect" do - test "undefined format. it redirects to feed", %{conn: conn} do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - response = - conn - |> put_req_header("accept", "application/xml") - |> get("/users/#{user.nickname}") - |> response(302) - - assert response == - "You are being redirected." - end - - test "undefined format. it returns error when user not found", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/xml") - |> get("/users/jimm") - |> response(404) - - assert response == ~S({"error":"Not found"}) - end - - test "activity+json format. it redirects on actual feed of user", %{conn: conn} do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - response = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}") - |> json_response(200) - - assert response["endpoints"] == %{ - "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize", - "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps", - "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token", - "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox", - "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media" - } - - assert response["@context"] == [ - "https://www.w3.org/ns/activitystreams", - "http://localhost:4001/schemas/litepub-0.1.jsonld", - %{"@language" => "und"} - ] - - assert Map.take(response, [ - "followers", - "following", - "id", - "inbox", - "manuallyApprovesFollowers", - "name", - "outbox", - "preferredUsername", - "summary", - "tag", - "type", - "url" - ]) == %{ - "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers", - "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following", - "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}", - "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox", - "manuallyApprovesFollowers" => false, - "name" => user.name, - "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox", - "preferredUsername" => user.nickname, - "summary" => user.bio, - "tag" => [], - "type" => "Person", - "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}" - } - end - - test "activity+json format. it returns error whe use not found", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/users/jimm") - |> json_response(404) - - assert response == "Not found" - end - - test "json format. it redirects on actual feed of user", %{conn: conn} do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - response = - conn - |> put_req_header("accept", "application/json") - |> get("/users/#{user.nickname}") - |> json_response(200) - - assert response["endpoints"] == %{ - "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize", - "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps", - "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token", - "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox", - "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media" - } - - assert response["@context"] == [ - "https://www.w3.org/ns/activitystreams", - "http://localhost:4001/schemas/litepub-0.1.jsonld", - %{"@language" => "und"} - ] - - assert Map.take(response, [ - "followers", - "following", - "id", - "inbox", - "manuallyApprovesFollowers", - "name", - "outbox", - "preferredUsername", - "summary", - "tag", - "type", - "url" - ]) == %{ - "followers" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/followers", - "following" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/following", - "id" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}", - "inbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/inbox", - "manuallyApprovesFollowers" => false, - "name" => user.name, - "outbox" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}/outbox", - "preferredUsername" => user.nickname, - "summary" => user.bio, - "tag" => [], - "type" => "Person", - "url" => "#{Pleroma.Web.base_url()}/users/#{user.nickname}" - } - end - - test "json format. it returns error whe use not found", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/json") - |> get("/users/jimm") - |> json_response(404) - - assert response == "Not found" - end - - test "html format. it redirects on actual feed of user", %{conn: conn} do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - response = - conn - |> get("/users/#{user.nickname}") - |> response(200) - - assert response == - Fallback.RedirectController.redirector_with_meta( - conn, - %{user: user} - ).resp_body - end - - test "html format. it returns error when user not found", %{conn: conn} do - response = - conn - |> get("/users/jimm") - |> json_response(404) - - assert response == %{"error" => "Not found"} - end - end - describe "GET /notice/:id/embed_player" do test "render embed player", %{conn: conn} do note_activity = insert(:note_activity) diff --git a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs index 7eaeda4a0..8a6528cbb 100644 --- a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs +++ b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do alias Pleroma.Conversation.Participation alias Pleroma.Notification alias Pleroma.Repo + alias Pleroma.User alias Pleroma.Web.CommonAPI import Pleroma.Factory @@ -73,6 +74,7 @@ test "PATCH /api/v1/pleroma/conversations/:id", %{conn: conn} do participation = Repo.preload(participation, :recipients) + user = User.get_cached_by_id(user.id) assert [user] == participation.recipients assert other_user not in participation.recipients diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index 56e318182..9d4cb70f0 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -81,19 +81,21 @@ test "it imports new-style mastodon follow lists", %{conn: conn} do assert response == "job started" end - test "requires 'follow' permission", %{conn: conn} do + test "requires 'follow' or 'write:follows' permissions", %{conn: conn} do token1 = insert(:oauth_token, scopes: ["read", "write"]) token2 = insert(:oauth_token, scopes: ["follow"]) + token3 = insert(:oauth_token, scopes: ["something"]) another_user = insert(:user) - for token <- [token1, token2] do + for token <- [token1, token2, token3] do conn = conn |> put_req_header("authorization", "Bearer #{token.token}") |> post("/api/pleroma/follow_import", %{"list" => "#{another_user.ap_id}"}) - if token == token1 do - assert %{"error" => "Insufficient permissions: follow."} == json_response(conn, 403) + if token == token3 do + assert %{"error" => "Insufficient permissions: follow | write:follows."} == + json_response(conn, 403) else assert json_response(conn, 200) end