diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dc85eaba2..5d0d3316a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,14 +10,16 @@ variables: &global_variables cache: &global_cache_policy key: ${CI_COMMIT_REF_SLUG} paths: - - deps - - _build + - deps + - _build + stages: - build - test - benchmark - deploy - release + - docker before_script: - mix local.hex --force @@ -264,3 +266,66 @@ arm64-musl: variables: *release-variables before_script: *before-release-musl script: *release + +docker: + stage: docker + image: docker:latest + cache: {} + dependencies: [] + variables: &docker-variables + DOCKER_DRIVER: overlay2 + DOCKER_HOST: unix:///var/run/docker.sock + IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA + IMAGE_TAG_SLUG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG + IMAGE_TAG_LATEST: $CI_REGISTRY_IMAGE:latest + IMAGE_TAG_LATEST_STABLE: $CI_REGISTRY_IMAGE:latest-stable + before_script: &before-docker + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + - docker pull $IMAGE_TAG_SLUG || true + - export CI_JOB_TIMESTAMP=$(date --utc -Iseconds) + - export CI_VCS_REF=$CI_COMMIT_SHORT_SHA + allow_failure: true + script: + - docker build --cache-from $IMAGE_TAG_SLUG --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP -t $IMAGE_TAG -t $IMAGE_TAG_SLUG -t $IMAGE_TAG_LATEST . + - docker push $IMAGE_TAG + - docker push $IMAGE_TAG_SLUG + - docker push $IMAGE_TAG_LATEST + tags: + - dind + only: + - develop@pleroma/pleroma + +docker-stable: + stage: docker + image: docker:latest + cache: {} + dependencies: [] + variables: *docker-variables + before_script: *before-docker + allow_failure: true + script: + - docker build --cache-from $IMAGE_TAG_SLUG --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP -t $IMAGE_TAG -t $IMAGE_TAG_SLUG -t $IMAGE_TAG_LATEST_STABLE . + - docker push $IMAGE_TAG + - docker push $IMAGE_TAG_SLUG + - docker push $IMAGE_TAG_LATEST_STABLE + tags: + - dind + only: + - stable@pleroma/pleroma + +docker-release: + stage: docker + image: docker:latest + cache: {} + dependencies: [] + variables: *docker-variables + before_script: *before-docker + allow_failure: true + script: + - docker build --cache-from $IMAGE_TAG_SLUG --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP -t $IMAGE_TAG -t $IMAGE_TAG_SLUG . + - docker push $IMAGE_TAG + - docker push $IMAGE_TAG_SLUG + tags: + - dind + only: + - /^release/.*$/@pleroma/pleroma diff --git a/CHANGELOG.md b/CHANGELOG.md index 397348304..09f31d5a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Breaking**: MDII uploader ### Changed +- **Breaking:** Pleroma won't start if it detects unapplied migrations - **Breaking:** attachments are removed along with statuses when there are no other references to it - **Breaking:** Elixir >=1.8 is now required (was >= 1.7) - **Breaking:** attachment links (`config :pleroma, :instance, no_attachment_links` and `config :pleroma, Pleroma.Upload, link_name`) disabled by default +- **Breaking:** OAuth: defaulted `[:auth, :enforce_oauth_admin_scope_usage]` setting to `true` which demands `admin` OAuth scope to perform admin actions (in addition to `is_admin` flag on User); make sure to use bundled or newer versions of AdminFE & PleromaFE to access admin / moderator features. - Replaced [pleroma_job_queue](https://git.pleroma.social/pleroma/pleroma_job_queue) and `Pleroma.Web.Federator.RetryQueue` with [Oban](https://github.com/sorentwo/oban) (see [`docs/config.md`](docs/config.md) on migrating customized worker / retry settings) - Introduced [quantum](https://github.com/quantum-elixir/quantum-core) job scheduler - Enabled `:instance, extended_nickname_format` in the default config @@ -42,6 +44,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Mastodon API, streaming: Add `pleroma.direct_conversation_id` to the `conversation` stream event payload. - Admin API: Render whole status in grouped reports - Mastodon API: User timelines will now respect blocks, unless you are getting the user timeline of somebody you blocked (which would be empty otherwise). +- Mastodon API: Favoriting / Repeating a post multiple times will now return the identical response every time. Before, executing that action twice would return an error ("already favorited") on the second try. ### Added @@ -90,6 +93,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Mastodon API: `/api/v1/update_credentials` accepts `actor_type` field. - Captcha: Support native provider - Captcha: Enable by default +- Mastodon API: Add support for `account_id` param to filter notifications by the account +- Mastodon API: Add `emoji_reactions` property to Statuses ### Fixed diff --git a/Dockerfile b/Dockerfile index c61dcfde9..4f7f12716 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,20 @@ RUN apk add git gcc g++ musl-dev make &&\ FROM alpine:3.9 +ARG BUILD_DATE +ARG VCS_REF + +LABEL maintainer="ops@pleroma.social" \ + org.opencontainers.image.title="pleroma" \ + org.opencontainers.image.description="Pleroma for Docker" \ + org.opencontainers.image.authors="ops@pleroma.social" \ + org.opencontainers.image.vendor="pleroma.social" \ + org.opencontainers.image.documentation="https://git.pleroma.social/pleroma/pleroma" \ + org.opencontainers.image.licenses="AGPL-3.0" \ + org.opencontainers.image.url="https://pleroma.social" \ + org.opencontainers.image.revision=$VCS_REF \ + org.opencontainers.image.created=$BUILD_DATE + ARG HOME=/opt/pleroma ARG DATA=/var/lib/pleroma diff --git a/benchmarks/load_testing/generator.ex b/benchmarks/load_testing/generator.ex index a957e0ffb..3f88fefd7 100644 --- a/benchmarks/load_testing/generator.ex +++ b/benchmarks/load_testing/generator.ex @@ -9,7 +9,7 @@ def generate_like_activities(user, posts) do {time, _} = :timer.tc(fn -> Task.async_stream( - Enum.take_random(posts, count_likes), + Enum.take_random(posts, count_likes), fn post -> {:ok, _, _} = CommonAPI.favorite(post.id, user) end, max_concurrency: 10, timeout: 30_000 @@ -142,6 +142,48 @@ defp do_generate_activity(users) do CommonAPI.post(Enum.random(users), post) end + def generate_power_intervals(opts \\ []) do + count = Keyword.get(opts, :count, 20) + power = Keyword.get(opts, :power, 2) + IO.puts("Generating #{count} intervals for a power #{power} series...") + counts = Enum.map(1..count, fn n -> :math.pow(n, power) end) + sum = Enum.sum(counts) + + densities = + Enum.map(counts, fn c -> + c / sum + end) + + densities + |> Enum.reduce(0, fn density, acc -> + if acc == 0 do + [{0, density}] + else + [{_, lower} | _] = acc + [{lower, lower + density} | acc] + end + end) + |> Enum.reverse() + end + + def generate_tagged_activities(opts \\ []) do + tag_count = Keyword.get(opts, :tag_count, 20) + users = Keyword.get(opts, :users, Repo.all(User)) + activity_count = Keyword.get(opts, :count, 200_000) + + intervals = generate_power_intervals(count: tag_count) + + IO.puts( + "Generating #{activity_count} activities using #{tag_count} different tags of format `tag_n`, starting at tag_0" + ) + + Enum.each(1..activity_count, fn _ -> + random = :rand.uniform() + i = Enum.find_index(intervals, fn {lower, upper} -> lower <= random && upper > random end) + CommonAPI.post(Enum.random(users), %{"status" => "a post with the tag #tag_#{i}"}) + end) + end + defp do_generate_activity_with_mention(user, users) do mentions_cnt = Enum.random([2, 3, 4, 5]) with_user = Enum.random([true, false]) diff --git a/benchmarks/mix/tasks/pleroma/benchmarks/tags.ex b/benchmarks/mix/tasks/pleroma/benchmarks/tags.ex new file mode 100644 index 000000000..fd1506907 --- /dev/null +++ b/benchmarks/mix/tasks/pleroma/benchmarks/tags.ex @@ -0,0 +1,87 @@ +defmodule Mix.Tasks.Pleroma.Benchmarks.Tags do + use Mix.Task + alias Pleroma.Repo + alias Pleroma.LoadTesting.Generator + import Ecto.Query + + def run(_args) do + Mix.Pleroma.start_pleroma() + activities_count = Repo.aggregate(from(a in Pleroma.Activity), :count, :id) + + if activities_count == 0 do + IO.puts("Did not find any activities, cleaning and generating") + clean_tables() + Generator.generate_users(users_max: 10) + Generator.generate_tagged_activities() + else + IO.puts("Found #{activities_count} activities, won't generate new ones") + end + + tags = Enum.map(0..20, fn i -> {"For #tag_#{i}", "tag_#{i}"} end) + + Enum.each(tags, fn {_, tag} -> + query = + from(o in Pleroma.Object, + where: fragment("(?)->'tag' \\? (?)", o.data, ^tag) + ) + + count = Repo.aggregate(query, :count, :id) + IO.puts("Database contains #{count} posts tagged with #{tag}") + end) + + user = Repo.all(Pleroma.User) |> List.first() + + Benchee.run( + %{ + "Hashtag fetching, any" => fn tags -> + Pleroma.Web.MastodonAPI.TimelineController.hashtag_fetching( + %{ + "any" => tags + }, + user, + false + ) + end, + # Will always return zero results because no overlapping hashtags are generated. + "Hashtag fetching, all" => fn tags -> + Pleroma.Web.MastodonAPI.TimelineController.hashtag_fetching( + %{ + "all" => tags + }, + user, + false + ) + end + }, + inputs: + tags + |> Enum.map(fn {_, v} -> v end) + |> Enum.chunk_every(2) + |> Enum.map(fn tags -> {"For #{inspect(tags)}", tags} end), + time: 5 + ) + + Benchee.run( + %{ + "Hashtag fetching" => fn tag -> + Pleroma.Web.MastodonAPI.TimelineController.hashtag_fetching( + %{ + "tag" => tag + }, + user, + false + ) + end + }, + inputs: tags, + time: 5 + ) + end + + defp clean_tables do + IO.puts("Deleting old data...\n") + Ecto.Adapters.SQL.query!(Repo, "TRUNCATE users CASCADE;") + Ecto.Adapters.SQL.query!(Repo, "TRUNCATE activities CASCADE;") + Ecto.Adapters.SQL.query!(Repo, "TRUNCATE objects CASCADE;") + end +end diff --git a/config/config.exs b/config/config.exs index 8b8ecc833..d706adc6e 100644 --- a/config/config.exs +++ b/config/config.exs @@ -488,7 +488,8 @@ mailer: 10, transmogrifier: 20, scheduled_activities: 10, - background: 5 + background: 5, + attachments_cleanup: 5 ], crontab: [ {"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker}, @@ -553,7 +554,7 @@ config :pleroma, :auth, - enforce_oauth_admin_scope_usage: false, + enforce_oauth_admin_scope_usage: true, oauth_consumer_strategies: oauth_consumer_strategies config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail, enabled: false diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md index bb62ed5f2..50076cf98 100644 --- a/docs/API/differences_in_mastoapi_responses.md +++ b/docs/API/differences_in_mastoapi_responses.md @@ -29,6 +29,7 @@ Has these additional fields under the `pleroma` object: - `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain` - `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire - `thread_muted`: true if the thread the post belongs to is muted +- `emoji_reactions`: An object with all the emoji reactions with count. Contains no information about the reacting users, for that use the `emoji_reactions_by` endpoint. ## Attachments diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 510d3273c..896cbb3c5 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -312,9 +312,7 @@ def restrict_deactivated_users(query) do from(u in User.Query.build(deactivated: true), select: u.ap_id) |> Repo.all() - from(activity in query, - where: activity.actor not in ^deactivated_users - ) + Activity.Queries.exclude_authors(query, deactivated_users) end defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search diff --git a/lib/pleroma/activity/queries.ex b/lib/pleroma/activity/queries.ex index 26bc1099d..79f305201 100644 --- a/lib/pleroma/activity/queries.ex +++ b/lib/pleroma/activity/queries.ex @@ -12,6 +12,7 @@ defmodule Pleroma.Activity.Queries do @type query :: Ecto.Queryable.t() | Activity.t() alias Pleroma.Activity + alias Pleroma.User @spec by_ap_id(query, String.t()) :: query def by_ap_id(query \\ Activity, ap_id) do @@ -29,6 +30,11 @@ def by_actor(query \\ Activity, actor) do ) end + @spec by_author(query, String.t()) :: query + def by_author(query \\ Activity, %User{ap_id: ap_id}) do + from(a in query, where: a.actor == ^ap_id) + end + @spec by_object_id(query, String.t() | [String.t()]) :: query def by_object_id(query \\ Activity, object_id) @@ -72,4 +78,8 @@ def exclude_type(query \\ Activity, activity_type) do where: fragment("(?)->>'type' != ?", activity.data, ^activity_type) ) end + + def exclude_authors(query \\ Activity, actors) do + from(activity in query, where: activity.actor not in ^actors) + end end diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex index d30a5a6a5..f96e208da 100644 --- a/lib/pleroma/activity/search.ex +++ b/lib/pleroma/activity/search.ex @@ -26,18 +26,23 @@ def search(user, search_query, options \\ []) do |> query_with(index_type, search_query) |> maybe_restrict_local(user) |> maybe_restrict_author(author) + |> maybe_restrict_blocked(user) |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => limit}, :offset) |> maybe_fetch(user, search_query) end def maybe_restrict_author(query, %User{} = author) do - from([a, o] in query, - where: a.actor == ^author.ap_id - ) + Activity.Queries.by_author(query, author) end def maybe_restrict_author(query, _), do: query + def maybe_restrict_blocked(query, %User{} = user) do + Activity.Queries.exclude_authors(query, User.blocked_users_ap_ids(user)) + end + + def maybe_restrict_blocked(query, _), do: query + defp restrict_public(q) do from([a, o] in q, where: fragment("?->>'type' = 'Create'", a.data), diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 98d7a6e86..6fdc54aed 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -33,6 +33,7 @@ def user_agent do def start(_type, _args) do Pleroma.HTML.compile_scrubbers() Pleroma.Config.DeprecationWarnings.warn() + Pleroma.Repo.check_migrations_applied!() setup_instrumenters() load_custom_modules() diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 2452a7389..38e372f6d 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -19,6 +19,8 @@ defmodule Pleroma.Object do @type t() :: %__MODULE__{} + @derive {Jason.Encoder, only: [:data]} + schema "objects" do field(:data, :map) @@ -180,85 +182,17 @@ def swap_object_with_tombstone(object) do def delete(%Object{data: %{"id" => id}} = object) do with {:ok, _obj} = swap_object_with_tombstone(object), - :ok <- delete_attachments(object), deleted_activity = Activity.delete_all_by_object_ap_id(id), {:ok, true} <- Cachex.del(:object_cache, "object:#{id}"), - {:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path) do + {:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path), + {:ok, _} <- + Pleroma.Workers.AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{ + "object" => object + }) do {:ok, object, deleted_activity} end end - defp delete_attachments(%{data: %{"attachment" => [_ | _] = attachments, "actor" => actor}}) do - hrefs = - Enum.flat_map(attachments, fn attachment -> - Enum.map(attachment["url"], & &1["href"]) - end) - - names = Enum.map(attachments, & &1["name"]) - - uploader = Pleroma.Config.get([Pleroma.Upload, :uploader]) - - # find all objects for copies of the attachments, name and actor doesn't matter here - delete_ids = - from(o in Object, - where: - fragment( - "to_jsonb(array(select jsonb_array_elements((?)#>'{url}') ->> 'href'))::jsonb \\?| (?)", - o.data, - ^hrefs - ) - ) - |> Repo.all() - # we should delete 1 object for any given attachment, but don't delete files if - # there are more than 1 object for it - |> Enum.reduce(%{}, fn %{ - id: id, - data: %{ - "url" => [%{"href" => href}], - "actor" => obj_actor, - "name" => name - } - }, - acc -> - Map.update(acc, href, %{id: id, count: 1}, fn val -> - case obj_actor == actor and name in names do - true -> - # set id of the actor's object that will be deleted - %{val | id: id, count: val.count + 1} - - false -> - # another actor's object, just increase count to not delete file - %{val | count: val.count + 1} - end - end) - end) - |> Enum.map(fn {href, %{id: id, count: count}} -> - # only delete files that have single instance - with 1 <- count do - prefix = - case Pleroma.Config.get([Pleroma.Upload, :base_url]) do - nil -> "media" - _ -> "" - end - - base_url = Pleroma.Config.get([__MODULE__, :base_url], Pleroma.Web.base_url()) - - file_path = String.trim_leading(href, "#{base_url}/#{prefix}") - - uploader.delete_file(file_path) - end - - id - end) - - from(o in Object, where: o.id in ^delete_ids) - |> Repo.delete_all() - - :ok - end - - defp delete_attachments(%{data: _data}), do: :ok - def prune(%Object{data: %{"id" => id}} = object) do with {:ok, object} <- Repo.delete(object), {:ok, true} <- Cachex.del(:object_cache, "object:#{id}"), diff --git a/lib/pleroma/plugs/user_is_admin_plug.ex b/lib/pleroma/plugs/user_is_admin_plug.ex index 582fb1f92..3190163d3 100644 --- a/lib/pleroma/plugs/user_is_admin_plug.ex +++ b/lib/pleroma/plugs/user_is_admin_plug.ex @@ -23,6 +23,7 @@ def call(%{assigns: %{user: %User{is_admin: true}} = assigns} = conn, _) do token && OAuth.Scopes.contains_admin_scopes?(token.scopes) -> # Note: checking for _any_ admin scope presence, not necessarily fitting requested action. # Thus, controller must explicitly invoke OAuthScopesPlug to verify scope requirements. + # Admin might opt out of admin scope for some apps to block any admin actions from them. conn true -> diff --git a/lib/pleroma/repo.ex b/lib/pleroma/repo.ex index f57e088bc..cb0b6653c 100644 --- a/lib/pleroma/repo.ex +++ b/lib/pleroma/repo.ex @@ -8,6 +8,8 @@ defmodule Pleroma.Repo do adapter: Ecto.Adapters.Postgres, migration_timestamps: [type: :naive_datetime_usec] + require Logger + defmodule Instrumenter do use Prometheus.EctoInstrumenter end @@ -47,4 +49,37 @@ def get_assoc(resource, association) do _ -> {:error, :not_found} end end + + def check_migrations_applied!() do + unless Pleroma.Config.get( + [:i_am_aware_this_may_cause_data_loss, :disable_migration_check], + false + ) do + Ecto.Migrator.with_repo(__MODULE__, fn repo -> + down_migrations = + Ecto.Migrator.migrations(repo) + |> Enum.reject(fn + {:up, _, _} -> true + {:down, _, _} -> false + end) + + if length(down_migrations) > 0 do + down_migrations_text = + Enum.map(down_migrations, fn {:down, id, name} -> "- #{name} (#{id})\n" end) + + Logger.error( + "The following migrations were not applied:\n#{down_migrations_text}If you want to start Pleroma anyway, set\nconfig :pleroma, :i_am_aware_this_may_cause_data_loss, disable_migration_check: true" + ) + + raise Pleroma.Repo.UnappliedMigrationsError + end + end) + else + :ok + end + end +end + +defmodule Pleroma.Repo.UnappliedMigrationsError do + defexception message: "Unapplied Migrations detected" end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 2e225415c..430f04ae9 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1874,22 +1874,13 @@ defp truncate_field(%{"name" => name, "value" => value}) do end def admin_api_update(user, params) do - changeset = - cast(user, params, [ - :is_moderator, - :is_admin, - :show_role - ]) - - with {:ok, updated_user} <- update_and_set_cache(changeset) do - if user.is_admin != updated_user.is_admin do - # Admin status change results in change of accessible OAuth scopes, and instead of changing - # already issued tokens we revoke them, requiring user to sign in again - global_sign_out(user) - end - - {:ok, updated_user} - end + user + |> cast(params, [ + :is_moderator, + :is_admin, + :show_role + ]) + |> update_and_set_cache() end @doc "Signs user out of all applications" diff --git a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex index 4eaea00d8..c184c3b66 100644 --- a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex @@ -20,7 +20,7 @@ def filter(%{"type" => message_type} = message) do with accepted_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :accept]), rejected_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :reject]), true <- - length(accepted_vocabulary) == 0 || Enum.member?(accepted_vocabulary, message_type), + Enum.empty?(accepted_vocabulary) || Enum.member?(accepted_vocabulary, message_type), false <- length(rejected_vocabulary) > 0 && Enum.member?(rejected_vocabulary, message_type), {:ok, _} <- filter(message["object"]) do diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 3fa789d53..2b8bfc3bd 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -658,24 +658,8 @@ def handle_incoming( with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object) - locked = new_user_data[:locked] || false - attachment = get_in(new_user_data, [:source_data, "attachment"]) || [] - invisible = new_user_data[:invisible] || false - - fields = - attachment - |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end) - |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end) - - update_data = - new_user_data - |> Map.take([:avatar, :banner, :bio, :name, :also_known_as]) - |> Map.put(:fields, fields) - |> Map.put(:locked, locked) - |> Map.put(:invisible, invisible) - actor - |> User.upgrade_changeset(update_data, true) + |> User.upgrade_changeset(new_user_data, true) |> User.update_and_set_cache() ActivityPub.update(%{ diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index c8abeff06..7118faf94 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -32,19 +32,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, %{scopes: ["read:accounts"], admin: true} - when action in [:list_users, :user_show, :right_get, :invites] + when action in [:list_users, :user_show, :right_get] ) plug( OAuthScopesPlug, %{scopes: ["write:accounts"], admin: true} when action in [ - :get_invite_token, - :revoke_invite, - :email_invite, :get_password_reset, - :user_follow, - :user_unfollow, :user_delete, :users_create, :user_toggle_activation, @@ -57,6 +52,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do ] ) + plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :invites) + + plug( + OAuthScopesPlug, + %{scopes: ["write:invites"], admin: true} + when action in [:create_invite_token, :revoke_invite, :email_invite] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:follows"], admin: true} + when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow] + ) + plug( OAuthScopesPlug, %{scopes: ["read:reports"], admin: true} @@ -66,7 +75,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, %{scopes: ["write:reports"], admin: true} - when action in [:report_update_state, :report_respond] + when action in [:reports_update] ) plug( @@ -90,7 +99,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, %{scopes: ["write"], admin: true} - when action in [:relay_follow, :relay_unfollow, :config_update] + when action == :config_update ) @users_page_size 50 @@ -630,7 +639,7 @@ def get_password_reset(conn, %{"nickname" => nickname}) do def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do users = nicknames |> Enum.map(&User.get_cached_by_nickname/1) - Enum.map(users, &User.force_password_reset_async/1) + Enum.each(users, &User.force_password_reset_async/1) ModerationLog.insert_log(%{ actor: admin, diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 2f3bcfc3c..c05a6c544 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -85,9 +85,13 @@ def delete(activity_id, user) do def repeat(id_or_ap_id, user, params \\ %{}) do with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), object <- Object.normalize(activity), - nil <- Utils.get_existing_announce(user.ap_id, object), + announce_activity <- Utils.get_existing_announce(user.ap_id, object), public <- public_announce?(object, params) do - ActivityPub.announce(user, object, nil, true, public) + if announce_activity do + {:ok, announce_activity, object} + else + ActivityPub.announce(user, object, nil, true, public) + end else _ -> {:error, dgettext("errors", "Could not repeat")} end @@ -105,8 +109,12 @@ def unrepeat(id_or_ap_id, user) do def favorite(id_or_ap_id, user) do with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), object <- Object.normalize(activity), - nil <- Utils.get_existing_like(user.ap_id, object) do - ActivityPub.like(user, object) + like_activity <- Utils.get_existing_like(user.ap_id, object) do + if like_activity do + {:ok, like_activity, object} + else + ActivityPub.like(user, object) + end else _ -> {:error, dgettext("errors", "Could not favorite")} end diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index 16759be6a..f2508aca4 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -23,6 +23,23 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) # GET /api/v1/notifications + def index(conn, %{"account_id" => account_id} = params) do + case Pleroma.User.get_cached_by_id(account_id) do + %{ap_id: account_ap_id} -> + params = + params + |> Map.delete("account_id") + |> Map.put("account_ap_id", account_ap_id) + + index(conn, params) + + _ -> + conn + |> put_status(:not_found) + |> json(%{"error" => "Account is not found"}) + end + end + def index(%{assigns: %{user: user}} = conn, params) do notifications = MastodonAPI.get_notifications(user, params) diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index 0a929f55b..5a5db8e00 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -43,7 +43,7 @@ defp do_search(version, %{assigns: %{user: user}} = conn, %{"q" => query} = para result = default_values |> Enum.map(fn {resource, default_value} -> - if params["type"] == nil or params["type"] == resource do + if params["type"] in [nil, resource] do {resource, fn -> resource_search(version, resource, query, options) end} else {resource, fn -> default_value end} diff --git a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex index fc7d52824..11f7b85d3 100644 --- a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex @@ -6,9 +6,9 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do @moduledoc "The module represents functions to manage user subscriptions." use Pleroma.Web, :controller + alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View alias Pleroma.Web.Push alias Pleroma.Web.Push.Subscription - alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View action_fallback(:errors) diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 384159336..29964a1d4 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -77,10 +77,7 @@ def public(%{assigns: %{user: user}} = conn, params) do |> render("index.json", activities: activities, for: user, as: :activity) end - # GET /api/v1/timelines/tag/:tag - def hashtag(%{assigns: %{user: user}} = conn, params) do - local_only = truthy_param?(params["local"]) - + def hashtag_fetching(params, user, local_only) do tags = [params["tag"], params["any"]] |> List.flatten() @@ -98,7 +95,7 @@ def hashtag(%{assigns: %{user: user}} = conn, params) do |> Map.get("none", []) |> Enum.map(&String.downcase(&1)) - activities = + _activities = params |> Map.put("type", "Create") |> Map.put("local_only", local_only) @@ -109,6 +106,13 @@ def hashtag(%{assigns: %{user: user}} = conn, params) do |> Map.put("tag_all", tag_all) |> Map.put("tag_reject", tag_reject) |> ActivityPub.fetch_public_activities() + end + + # GET /api/v1/timelines/tag/:tag + def hashtag(%{assigns: %{user: user}} = conn, params) do + local_only = truthy_param?(params["local"]) + + activities = hashtag_fetching(params, user, local_only) conn |> add_link_headers(activities, %{"local" => local_only}) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index b1816370e..390a2b190 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -56,6 +56,7 @@ def get_notifications(user, params \\ %{}) do user |> Notification.for_user_query(options) |> restrict(:exclude_types, options) + |> restrict(:account_ap_id, options) |> Pagination.fetch_paginated(params) end @@ -71,7 +72,8 @@ defp cast_params(params) do exclude_visibilities: {:array, :string}, reblogs: :boolean, with_muted: :boolean, - with_move: :boolean + with_move: :boolean, + account_ap_id: :string } changeset = cast({%{}, param_types}, params, Map.keys(param_types)) @@ -88,5 +90,9 @@ defp restrict(query, :exclude_types, %{exclude_types: mastodon_types = [_ | _]}) |> where([q, a], not fragment("? @> ARRAY[?->>'type']::varchar[]", ^ap_types, a.data)) end + defp restrict(query, :account_ap_id, %{account_ap_id: account_ap_id}) do + where(query, [n, a], a.actor == ^account_ap_id) + end + defp restrict(query, _, _), do: query end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index e9590224b..b59ac39bc 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -253,6 +253,16 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} nil end + emoji_reactions = + with %{data: %{"reactions" => emoji_reactions}} <- object do + Enum.map(emoji_reactions, fn {emoji, users} -> + {emoji, length(users)} + end) + |> Enum.into(%{}) + else + _ -> %{} + end + %{ id: to_string(activity.id), uri: object.data["id"], @@ -293,7 +303,8 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} spoiler_text: %{"text/plain" => summary_plaintext}, expires_at: expires_at, direct_conversation_id: direct_conversation_id, - thread_muted: thread_muted? + thread_muted: thread_muted?, + emoji_reactions: emoji_reactions } } end diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 87acdec97..5292aedf2 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -14,10 +14,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do alias Pleroma.Web.ControllerHelper alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Authorization + alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken - alias Pleroma.Web.OAuth.Scopes require Logger @@ -222,7 +222,7 @@ def token_exchange( {:user_active, true} <- {:user_active, !user.deactivated}, {:password_reset_pending, false} <- {:password_reset_pending, user.password_reset_pending}, - {:ok, scopes} <- validate_scopes(app, params, user), + {:ok, scopes} <- validate_scopes(app, params), {:ok, auth} <- Authorization.create_authorization(app, user, scopes), {:ok, token} <- Token.exchange_token(app, auth) do json(conn, Token.Response.build(user, token)) @@ -471,7 +471,7 @@ defp do_create_authorization( {:get_user, (user && {:ok, user}) || Authenticator.get_user(conn)}, %App{} = app <- Repo.get_by(App, client_id: client_id), true <- redirect_uri in String.split(app.redirect_uris), - {:ok, scopes} <- validate_scopes(app, auth_attrs, user), + {:ok, scopes} <- validate_scopes(app, auth_attrs), {:auth_active, true} <- {:auth_active, User.auth_active?(user)} do Authorization.create_authorization(app, user, scopes) end @@ -487,12 +487,12 @@ defp get_session_registration_id(%Plug.Conn{} = conn), do: get_session(conn, :re defp put_session_registration_id(%Plug.Conn{} = conn, registration_id), do: put_session(conn, :registration_id, registration_id) - @spec validate_scopes(App.t(), map(), User.t()) :: + @spec validate_scopes(App.t(), map()) :: {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes} - defp validate_scopes(%App{} = app, params, %User{} = user) do + defp validate_scopes(%App{} = app, params) do params |> Scopes.fetch_scopes(app.scopes) - |> Scopes.validate(app.scopes, user) + |> 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 00da225b9..151467494 100644 --- a/lib/pleroma/web/oauth/scopes.ex +++ b/lib/pleroma/web/oauth/scopes.ex @@ -8,7 +8,6 @@ defmodule Pleroma.Web.OAuth.Scopes do """ alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.User @doc """ Fetch scopes from request params. @@ -56,35 +55,18 @@ def to_string(scopes), do: Enum.join(scopes, " ") @doc """ Validates scopes. """ - @spec validate(list() | nil, list(), User.t()) :: + @spec validate(list() | nil, list()) :: {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes} - def validate(blank_scopes, _app_scopes, _user) when blank_scopes in [nil, []], + def validate(blank_scopes, _app_scopes) when blank_scopes in [nil, []], do: {:error, :missing_scopes} - def validate(scopes, app_scopes, %User{} = user) do - with {:ok, _} <- ensure_scopes_support(scopes, app_scopes), - {:ok, scopes} <- authorize_admin_scopes(scopes, app_scopes, user) do - {:ok, scopes} - end - end - - defp ensure_scopes_support(scopes, app_scopes) do + def validate(scopes, app_scopes) do case OAuthScopesPlug.filter_descendants(scopes, app_scopes) do ^scopes -> {:ok, scopes} _ -> {:error, :unsupported_scopes} end end - defp authorize_admin_scopes(scopes, app_scopes, %User{} = user) do - if user.is_admin || !contains_admin_scopes?(scopes) || !contains_admin_scopes?(app_scopes) do - {:ok, scopes} - else - # Gracefully dropping admin scopes from requested scopes if user isn't an admin (not raising) - scopes = scopes -- OAuthScopesPlug.filter_descendants(scopes, ["admin"]) - validate(scopes, app_scopes, user) - end - end - def contains_admin_scopes?(scopes) do scopes |> OAuthScopesPlug.filter_descendants(["admin"]) 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 772c535a4..3285dc11b 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -23,7 +23,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do plug( OAuthScopesPlug, %{scopes: ["read:statuses"]} - when action in [:conversation, :conversation_statuses, :emoji_reactions_by] + when action in [:conversation, :conversation_statuses] ) plug( diff --git a/lib/pleroma/workers/attachments_cleanup_worker.ex b/lib/pleroma/workers/attachments_cleanup_worker.ex new file mode 100644 index 000000000..3f421db40 --- /dev/null +++ b/lib/pleroma/workers/attachments_cleanup_worker.ex @@ -0,0 +1,88 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.AttachmentsCleanupWorker do + import Ecto.Query + + alias Pleroma.Object + alias Pleroma.Repo + + use Pleroma.Workers.WorkerHelper, queue: "attachments_cleanup" + + @impl Oban.Worker + def perform( + %{"object" => %{"data" => %{"attachment" => [_ | _] = attachments, "actor" => actor}}}, + _job + ) do + hrefs = + Enum.flat_map(attachments, fn attachment -> + Enum.map(attachment["url"], & &1["href"]) + end) + + names = Enum.map(attachments, & &1["name"]) + + uploader = Pleroma.Config.get([Pleroma.Upload, :uploader]) + + # find all objects for copies of the attachments, name and actor doesn't matter here + delete_ids = + from(o in Object, + where: + fragment( + "to_jsonb(array(select jsonb_array_elements((?)#>'{url}') ->> 'href' where jsonb_typeof((?)#>'{url}') = 'array'))::jsonb \\?| (?)", + o.data, + o.data, + ^hrefs + ) + ) + # The query above can be time consumptive on large instances until we + # refactor how uploads are stored + |> Repo.all(timout: :infinity) + # we should delete 1 object for any given attachment, but don't delete + # files if there are more than 1 object for it + |> Enum.reduce(%{}, fn %{ + id: id, + data: %{ + "url" => [%{"href" => href}], + "actor" => obj_actor, + "name" => name + } + }, + acc -> + Map.update(acc, href, %{id: id, count: 1}, fn val -> + case obj_actor == actor and name in names do + true -> + # set id of the actor's object that will be deleted + %{val | id: id, count: val.count + 1} + + false -> + # another actor's object, just increase count to not delete file + %{val | count: val.count + 1} + end + end) + end) + |> Enum.map(fn {href, %{id: id, count: count}} -> + # only delete files that have single instance + with 1 <- count do + prefix = + case Pleroma.Config.get([Pleroma.Upload, :base_url]) do + nil -> "media" + _ -> "" + end + + base_url = Pleroma.Config.get([__MODULE__, :base_url], Pleroma.Web.base_url()) + + file_path = String.trim_leading(href, "#{base_url}/#{prefix}") + + uploader.delete_file(file_path) + end + + id + end) + + from(o in Object, where: o.id in ^delete_ids) + |> Repo.delete_all() + end + + def perform(%{"object" => _object}, _job), do: :ok +end diff --git a/mix.exs b/mix.exs index a9ad3dab0..76bf2848e 100644 --- a/mix.exs +++ b/mix.exs @@ -123,7 +123,7 @@ defp deps do {:earmark, "~> 1.3"}, {:bbcode, "~> 0.1.1"}, {:ex_machina, "~> 2.3", only: :test}, - {:credo, "~> 0.9.3", only: [:dev, :test]}, + {:credo, "~> 1.1.0", only: [:dev, :test], runtime: false}, {:mock, "~> 0.3.3", only: :test}, {:crypt, git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"}, diff --git a/mix.lock b/mix.lock index 432d55e04..08ed8fb72 100644 --- a/mix.lock +++ b/mix.lock @@ -16,7 +16,7 @@ "cors_plug": {:hex, :cors_plug, "1.5.2", "72df63c87e4f94112f458ce9d25800900cc88608c1078f0e4faddf20933eda6e", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm"}, - "credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, + "credo": {:hex, :credo, "1.1.5", "caec7a3cadd2e58609d7ee25b3931b129e739e070539ad1a0cd7efeeb47014f4", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, "crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, "crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]}, "custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm"}, @@ -63,7 +63,7 @@ "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"}, "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"}, - "mock": {:hex, :mock, "0.3.3", "42a433794b1291a9cf1525c6d26b38e039e0d3a360732b5e467bfc77ef26c914", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"}, + "mock": {:hex, :mock, "0.3.4", "c5862eb3b8c64237f45f586cf00c9d892ba07bb48305a43319d428ce3c2897dd", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"}, "mogrify": {:hex, :mogrify, "0.6.1", "de1b527514f2d95a7bbe9642eb556061afb337e220cf97adbf3a4e6438ed70af", [:mix], [], "hexpm"}, "mox": {:hex, :mox, "0.5.1", "f86bb36026aac1e6f924a4b6d024b05e9adbed5c63e8daa069bd66fb3292165b", [:mix], [], "hexpm"}, "myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]}, diff --git a/priv/repo/migrations/20191220174645_add_scopes_to_pleroma_feo_auth_records.exs b/priv/repo/migrations/20191220174645_add_scopes_to_pleroma_feo_auth_records.exs new file mode 100644 index 000000000..6b160ad16 --- /dev/null +++ b/priv/repo/migrations/20191220174645_add_scopes_to_pleroma_feo_auth_records.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.AddScopesToPleromaFEOAuthRecords do + use Ecto.Migration + + def up do + update_scopes_clause = "SET scopes = '{read,write,follow,push,admin}'" + apps_where = "WHERE apps.client_name like 'PleromaFE_%' or apps.client_name like 'AdminFE_%'" + app_id_subquery_where = "WHERE app_id IN (SELECT apps.id FROM apps #{apps_where})" + + execute("UPDATE apps #{update_scopes_clause} #{apps_where}") + + for table <- ["oauth_authorizations", "oauth_tokens"] do + execute("UPDATE #{table} #{update_scopes_clause} #{app_id_subquery_where}") + end + end + + def down, do: :noop +end diff --git a/priv/static/adminfe/chunk-15fa.5a5f973d.css b/priv/static/adminfe/chunk-15fa.5a5f973d.css new file mode 100644 index 000000000..30bf7de23 Binary files /dev/null and b/priv/static/adminfe/chunk-15fa.5a5f973d.css differ diff --git a/priv/static/adminfe/chunk-18e1.dd09fe2e.css b/priv/static/adminfe/chunk-18e1.dd09fe2e.css new file mode 100644 index 000000000..da819ca09 Binary files /dev/null and b/priv/static/adminfe/chunk-18e1.dd09fe2e.css differ diff --git a/priv/static/adminfe/chunk-1ada.90dffac4.css b/priv/static/adminfe/chunk-1ada.90dffac4.css new file mode 100644 index 000000000..c0074e6f7 Binary files /dev/null and b/priv/static/adminfe/chunk-1ada.90dffac4.css differ diff --git a/priv/static/adminfe/chunk-2aa6.8ce63ac0.css b/priv/static/adminfe/chunk-2aa6.8ce63ac0.css new file mode 100644 index 000000000..8bd6a2e50 Binary files /dev/null and b/priv/static/adminfe/chunk-2aa6.8ce63ac0.css differ diff --git a/priv/static/adminfe/chunk-3d8e.48523459.css b/priv/static/adminfe/chunk-3d8e.48523459.css new file mode 100644 index 000000000..8eefd9493 Binary files /dev/null and b/priv/static/adminfe/chunk-3d8e.48523459.css differ diff --git a/priv/static/adminfe/chunk-4dc2.25d3bcab.css b/priv/static/adminfe/chunk-4dc2.25d3bcab.css new file mode 100644 index 000000000..48784b9d2 Binary files /dev/null and b/priv/static/adminfe/chunk-4dc2.25d3bcab.css differ diff --git a/priv/static/adminfe/chunk-6b7b.1ab4da3b.css b/priv/static/adminfe/chunk-6b7b.1ab4da3b.css new file mode 100644 index 000000000..7124c9690 Binary files /dev/null and b/priv/static/adminfe/chunk-6b7b.1ab4da3b.css differ diff --git a/priv/static/adminfe/chunk-6e77.57276c93.css b/priv/static/adminfe/chunk-6e77.57276c93.css new file mode 100644 index 000000000..f0b3bf144 Binary files /dev/null and b/priv/static/adminfe/chunk-6e77.57276c93.css differ diff --git a/priv/static/adminfe/chunk-7f83.f9f73c8e.css b/priv/static/adminfe/chunk-7f83.f9f73c8e.css new file mode 100644 index 000000000..29f7b475d Binary files /dev/null and b/priv/static/adminfe/chunk-7f83.f9f73c8e.css differ diff --git a/priv/static/adminfe/chunk-7f8e.1d10e1c7.css b/priv/static/adminfe/chunk-7f8e.1d10e1c7.css new file mode 100644 index 000000000..6cd674a28 Binary files /dev/null and b/priv/static/adminfe/chunk-7f8e.1d10e1c7.css differ diff --git a/priv/static/adminfe/chunk-elementUI.1abbc9b8.css b/priv/static/adminfe/chunk-elementUI.1abbc9b8.css new file mode 100644 index 000000000..c802d3a40 Binary files /dev/null and b/priv/static/adminfe/chunk-elementUI.1abbc9b8.css differ diff --git a/priv/static/adminfe/chunk-elementUI.a842fb0a.css b/priv/static/adminfe/chunk-elementUI.a842fb0a.css deleted file mode 100644 index 3fef5e5fd..000000000 Binary files a/priv/static/adminfe/chunk-elementUI.a842fb0a.css and /dev/null differ diff --git a/priv/static/adminfe/index.html b/priv/static/adminfe/index.html index d238accb5..e28125b5e 100644 --- a/priv/static/adminfe/index.html +++ b/priv/static/adminfe/index.html @@ -1 +1 @@ -Admin FE
\ No newline at end of file +Admin FE
\ No newline at end of file diff --git a/priv/static/adminfe/static/js/app.19b7049e.js b/priv/static/adminfe/static/js/app.19b7049e.js deleted file mode 100644 index d33589df4..000000000 Binary files a/priv/static/adminfe/static/js/app.19b7049e.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/app.19b7049e.js.map b/priv/static/adminfe/static/js/app.19b7049e.js.map deleted file mode 100644 index 90c7816c0..000000000 Binary files a/priv/static/adminfe/static/js/app.19b7049e.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/app.3da0f475.js b/priv/static/adminfe/static/js/app.3da0f475.js new file mode 100644 index 000000000..c6bd005d2 Binary files /dev/null and b/priv/static/adminfe/static/js/app.3da0f475.js differ diff --git a/priv/static/adminfe/static/js/app.3da0f475.js.map b/priv/static/adminfe/static/js/app.3da0f475.js.map new file mode 100644 index 000000000..999df6709 Binary files /dev/null and b/priv/static/adminfe/static/js/app.3da0f475.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-0cc4.35b47d0a.js b/priv/static/adminfe/static/js/chunk-0cc4.35b47d0a.js deleted file mode 100644 index 71b39bb28..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0cc4.35b47d0a.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0cc4.35b47d0a.js.map b/priv/static/adminfe/static/js/chunk-0cc4.35b47d0a.js.map deleted file mode 100644 index 4935ac41f..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0cc4.35b47d0a.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-15fa.10871dbf.js b/priv/static/adminfe/static/js/chunk-15fa.34070731.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-15fa.10871dbf.js rename to priv/static/adminfe/static/js/chunk-15fa.34070731.js index 8c7b033cf..937908d00 100644 Binary files a/priv/static/adminfe/static/js/chunk-15fa.10871dbf.js and b/priv/static/adminfe/static/js/chunk-15fa.34070731.js differ diff --git a/priv/static/adminfe/static/js/chunk-15fa.10871dbf.js.map b/priv/static/adminfe/static/js/chunk-15fa.34070731.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-15fa.10871dbf.js.map rename to priv/static/adminfe/static/js/chunk-15fa.34070731.js.map index a4b89ba68..d3830be7c 100644 Binary files a/priv/static/adminfe/static/js/chunk-15fa.10871dbf.js.map and b/priv/static/adminfe/static/js/chunk-15fa.34070731.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-18e1.9f7c9b0f.js b/priv/static/adminfe/static/js/chunk-18e1.c5abe3f2.js similarity index 97% rename from priv/static/adminfe/static/js/chunk-18e1.9f7c9b0f.js rename to priv/static/adminfe/static/js/chunk-18e1.c5abe3f2.js index 237c35b90..e0561aa1a 100644 Binary files a/priv/static/adminfe/static/js/chunk-18e1.9f7c9b0f.js and b/priv/static/adminfe/static/js/chunk-18e1.c5abe3f2.js differ diff --git a/priv/static/adminfe/static/js/chunk-18e1.9f7c9b0f.js.map b/priv/static/adminfe/static/js/chunk-18e1.c5abe3f2.js.map similarity index 98% rename from priv/static/adminfe/static/js/chunk-18e1.9f7c9b0f.js.map rename to priv/static/adminfe/static/js/chunk-18e1.c5abe3f2.js.map index 2cfd1cfcc..27f79dceb 100644 Binary files a/priv/static/adminfe/static/js/chunk-18e1.9f7c9b0f.js.map and b/priv/static/adminfe/static/js/chunk-18e1.c5abe3f2.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-9bb0.9c56835f.js b/priv/static/adminfe/static/js/chunk-1ada.b41cb585.js similarity index 97% rename from priv/static/adminfe/static/js/chunk-9bb0.9c56835f.js rename to priv/static/adminfe/static/js/chunk-1ada.b41cb585.js index 3e9a3e795..b7bc3b278 100644 Binary files a/priv/static/adminfe/static/js/chunk-9bb0.9c56835f.js and b/priv/static/adminfe/static/js/chunk-1ada.b41cb585.js differ diff --git a/priv/static/adminfe/static/js/chunk-9bb0.9c56835f.js.map b/priv/static/adminfe/static/js/chunk-1ada.b41cb585.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-9bb0.9c56835f.js.map rename to priv/static/adminfe/static/js/chunk-1ada.b41cb585.js.map index 929c7ed14..27a2f8e5c 100644 Binary files a/priv/static/adminfe/static/js/chunk-9bb0.9c56835f.js.map and b/priv/static/adminfe/static/js/chunk-1ada.b41cb585.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-2aa6.be23b313.js b/priv/static/adminfe/static/js/chunk-2aa6.be23b313.js new file mode 100644 index 000000000..21a0fde72 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-2aa6.be23b313.js differ diff --git a/priv/static/adminfe/static/js/chunk-2aa6.be23b313.js.map b/priv/static/adminfe/static/js/chunk-2aa6.be23b313.js.map new file mode 100644 index 000000000..64ac03193 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-2aa6.be23b313.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-3d8e.916ea1c1.js b/priv/static/adminfe/static/js/chunk-3d8e.916ea1c1.js new file mode 100644 index 000000000..57831d50d Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-3d8e.916ea1c1.js differ diff --git a/priv/static/adminfe/static/js/chunk-3d8e.916ea1c1.js.map b/priv/static/adminfe/static/js/chunk-3d8e.916ea1c1.js.map new file mode 100644 index 000000000..a66a5fc00 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-3d8e.916ea1c1.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-1c46.b92c7c1b.js b/priv/static/adminfe/static/js/chunk-4dc2.ec296292.js similarity index 98% rename from priv/static/adminfe/static/js/chunk-1c46.b92c7c1b.js rename to priv/static/adminfe/static/js/chunk-4dc2.ec296292.js index c63a38759..f7cbe90c4 100644 Binary files a/priv/static/adminfe/static/js/chunk-1c46.b92c7c1b.js and b/priv/static/adminfe/static/js/chunk-4dc2.ec296292.js differ diff --git a/priv/static/adminfe/static/js/chunk-1c46.b92c7c1b.js.map b/priv/static/adminfe/static/js/chunk-4dc2.ec296292.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-1c46.b92c7c1b.js.map rename to priv/static/adminfe/static/js/chunk-4dc2.ec296292.js.map index a0d4a6655..898961dd6 100644 Binary files a/priv/static/adminfe/static/js/chunk-1c46.b92c7c1b.js.map and b/priv/static/adminfe/static/js/chunk-4dc2.ec296292.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-6b7b.e7ed7973.js b/priv/static/adminfe/static/js/chunk-6b7b.e7ed7973.js new file mode 100644 index 000000000..3a2a7694b Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-6b7b.e7ed7973.js differ diff --git a/priv/static/adminfe/static/js/chunk-6b7b.e7ed7973.js.map b/priv/static/adminfe/static/js/chunk-6b7b.e7ed7973.js.map new file mode 100644 index 000000000..570df08e6 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-6b7b.e7ed7973.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-d01a.970cf312.js b/priv/static/adminfe/static/js/chunk-6e77.d1988eaf.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-d01a.970cf312.js rename to priv/static/adminfe/static/js/chunk-6e77.d1988eaf.js index 7bbd51e24..9fef6e5dc 100644 Binary files a/priv/static/adminfe/static/js/chunk-d01a.970cf312.js and b/priv/static/adminfe/static/js/chunk-6e77.d1988eaf.js differ diff --git a/priv/static/adminfe/static/js/chunk-d01a.970cf312.js.map b/priv/static/adminfe/static/js/chunk-6e77.d1988eaf.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-d01a.970cf312.js.map rename to priv/static/adminfe/static/js/chunk-6e77.d1988eaf.js.map index da3fbc95a..4b756b70a 100644 Binary files a/priv/static/adminfe/static/js/chunk-d01a.970cf312.js.map and b/priv/static/adminfe/static/js/chunk-6e77.d1988eaf.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-7de9.7b8cda50.js b/priv/static/adminfe/static/js/chunk-7f83.1321eab8.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-7de9.7b8cda50.js rename to priv/static/adminfe/static/js/chunk-7f83.1321eab8.js index 6311e5aa9..52c9efc2d 100644 Binary files a/priv/static/adminfe/static/js/chunk-7de9.7b8cda50.js and b/priv/static/adminfe/static/js/chunk-7f83.1321eab8.js differ diff --git a/priv/static/adminfe/static/js/chunk-7de9.7b8cda50.js.map b/priv/static/adminfe/static/js/chunk-7f83.1321eab8.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-7de9.7b8cda50.js.map rename to priv/static/adminfe/static/js/chunk-7f83.1321eab8.js.map index 2172dcc92..5a7d9d6a2 100644 Binary files a/priv/static/adminfe/static/js/chunk-7de9.7b8cda50.js.map and b/priv/static/adminfe/static/js/chunk-7f83.1321eab8.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-7f8e.2c3e63e9.js b/priv/static/adminfe/static/js/chunk-7f8e.0505d295.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-7f8e.2c3e63e9.js rename to priv/static/adminfe/static/js/chunk-7f8e.0505d295.js index 06988ef73..43d8a02d8 100644 Binary files a/priv/static/adminfe/static/js/chunk-7f8e.2c3e63e9.js and b/priv/static/adminfe/static/js/chunk-7f8e.0505d295.js differ diff --git a/priv/static/adminfe/static/js/chunk-7f8e.2c3e63e9.js.map b/priv/static/adminfe/static/js/chunk-7f8e.0505d295.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-7f8e.2c3e63e9.js.map rename to priv/static/adminfe/static/js/chunk-7f8e.0505d295.js.map index bf96082a8..d1100abb0 100644 Binary files a/priv/static/adminfe/static/js/chunk-7f8e.2c3e63e9.js.map and b/priv/static/adminfe/static/js/chunk-7f8e.0505d295.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-7fe2.458f9da5.js b/priv/static/adminfe/static/js/chunk-7fe2.458f9da5.js deleted file mode 100644 index ae8abe56d..000000000 Binary files a/priv/static/adminfe/static/js/chunk-7fe2.458f9da5.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-7fe2.458f9da5.js.map b/priv/static/adminfe/static/js/chunk-7fe2.458f9da5.js.map deleted file mode 100644 index 34a06172f..000000000 Binary files a/priv/static/adminfe/static/js/chunk-7fe2.458f9da5.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-a601.cc880efe.js b/priv/static/adminfe/static/js/chunk-a601.cc880efe.js deleted file mode 100644 index b30fe3033..000000000 Binary files a/priv/static/adminfe/static/js/chunk-a601.cc880efe.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-a601.cc880efe.js.map b/priv/static/adminfe/static/js/chunk-a601.cc880efe.js.map deleted file mode 100644 index 5d2358da2..000000000 Binary files a/priv/static/adminfe/static/js/chunk-a601.cc880efe.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-elementUI.2de79b84.js b/priv/static/adminfe/static/js/chunk-elementUI.2de79b84.js new file mode 100644 index 000000000..c76b0430b Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-elementUI.2de79b84.js differ diff --git a/priv/static/adminfe/static/js/chunk-elementUI.2de79b84.js.map b/priv/static/adminfe/static/js/chunk-elementUI.2de79b84.js.map new file mode 100644 index 000000000..fa9dc12f0 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-elementUI.2de79b84.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-elementUI.fa319e7b.js b/priv/static/adminfe/static/js/chunk-elementUI.fa319e7b.js deleted file mode 100644 index 90ae35a35..000000000 Binary files a/priv/static/adminfe/static/js/chunk-elementUI.fa319e7b.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-elementUI.fa319e7b.js.map b/priv/static/adminfe/static/js/chunk-elementUI.fa319e7b.js.map deleted file mode 100644 index 678122a98..000000000 Binary files a/priv/static/adminfe/static/js/chunk-elementUI.fa319e7b.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-f3c9.b3de53e2.js b/priv/static/adminfe/static/js/chunk-f3c9.b3de53e2.js deleted file mode 100644 index 1c301e14a..000000000 Binary files a/priv/static/adminfe/static/js/chunk-f3c9.b3de53e2.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-f3c9.b3de53e2.js.map b/priv/static/adminfe/static/js/chunk-f3c9.b3de53e2.js.map deleted file mode 100644 index 3b53caf53..000000000 Binary files a/priv/static/adminfe/static/js/chunk-f3c9.b3de53e2.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-libs.35c18287.js.map b/priv/static/adminfe/static/js/chunk-libs.35c18287.js.map deleted file mode 100644 index 0a3580834..000000000 Binary files a/priv/static/adminfe/static/js/chunk-libs.35c18287.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-libs.35c18287.js b/priv/static/adminfe/static/js/chunk-libs.680db3fc.js similarity index 94% rename from priv/static/adminfe/static/js/chunk-libs.35c18287.js rename to priv/static/adminfe/static/js/chunk-libs.680db3fc.js index 4b76d98e6..478edf880 100644 Binary files a/priv/static/adminfe/static/js/chunk-libs.35c18287.js and b/priv/static/adminfe/static/js/chunk-libs.680db3fc.js differ diff --git a/priv/static/adminfe/static/js/chunk-libs.680db3fc.js.map b/priv/static/adminfe/static/js/chunk-libs.680db3fc.js.map new file mode 100644 index 000000000..f8c92c2fa Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-libs.680db3fc.js.map differ diff --git a/priv/static/adminfe/static/js/runtime.cab03b3e.js b/priv/static/adminfe/static/js/runtime.cab03b3e.js new file mode 100644 index 000000000..f700f7ccc Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.cab03b3e.js differ diff --git a/priv/static/adminfe/static/js/runtime.cab03b3e.js.map b/priv/static/adminfe/static/js/runtime.cab03b3e.js.map new file mode 100644 index 000000000..eca2ddd29 Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.cab03b3e.js.map differ diff --git a/priv/static/adminfe/static/js/runtime.d6d1aaab.js b/priv/static/adminfe/static/js/runtime.d6d1aaab.js deleted file mode 100644 index ca1a91002..000000000 Binary files a/priv/static/adminfe/static/js/runtime.d6d1aaab.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.d6d1aaab.js.map b/priv/static/adminfe/static/js/runtime.d6d1aaab.js.map deleted file mode 100644 index b938f1ba6..000000000 Binary files a/priv/static/adminfe/static/js/runtime.d6d1aaab.js.map and /dev/null differ diff --git a/priv/static/index.html b/priv/static/index.html index 2467aa22a..b0aadb1a1 100644 --- a/priv/static/index.html +++ b/priv/static/index.html @@ -1 +1 @@ -Pleroma
\ No newline at end of file +Pleroma
\ No newline at end of file diff --git a/priv/static/static/font/fontello.1576166651574.woff b/priv/static/static/font/fontello.1576166651574.woff deleted file mode 100644 index bbffd6413..000000000 Binary files a/priv/static/static/font/fontello.1576166651574.woff and /dev/null differ diff --git a/priv/static/static/font/fontello.1576166651574.woff2 b/priv/static/static/font/fontello.1576166651574.woff2 deleted file mode 100644 index d35dce862..000000000 Binary files a/priv/static/static/font/fontello.1576166651574.woff2 and /dev/null differ diff --git a/priv/static/static/font/fontello.1576166651574.eot b/priv/static/static/font/fontello.1579102213354.eot similarity index 80% rename from priv/static/static/font/fontello.1576166651574.eot rename to priv/static/static/font/fontello.1579102213354.eot index fb27d4037..160cfa9f6 100644 Binary files a/priv/static/static/font/fontello.1576166651574.eot and b/priv/static/static/font/fontello.1579102213354.eot differ diff --git a/priv/static/static/font/fontello.1576166651574.svg b/priv/static/static/font/fontello.1579102213354.svg old mode 100755 new mode 100644 similarity index 89% rename from priv/static/static/font/fontello.1576166651574.svg rename to priv/static/static/font/fontello.1579102213354.svg index f5e497ce4..44beba9a2 --- a/priv/static/static/font/fontello.1576166651574.svg +++ b/priv/static/static/font/fontello.1579102213354.svg @@ -1,7 +1,7 @@ -Copyright (C) 2019 by original authors @ fontello.com +Copyright (C) 2020 by original authors @ fontello.com @@ -64,6 +64,18 @@ + + + + + + + + + + + + diff --git a/priv/static/static/font/fontello.1576166651574.ttf b/priv/static/static/font/fontello.1579102213354.ttf similarity index 80% rename from priv/static/static/font/fontello.1576166651574.ttf rename to priv/static/static/font/fontello.1579102213354.ttf index c49743ec6..44753f8c1 100644 Binary files a/priv/static/static/font/fontello.1576166651574.ttf and b/priv/static/static/font/fontello.1579102213354.ttf differ diff --git a/priv/static/static/font/fontello.1579102213354.woff b/priv/static/static/font/fontello.1579102213354.woff new file mode 100644 index 000000000..23351a090 Binary files /dev/null and b/priv/static/static/font/fontello.1579102213354.woff differ diff --git a/priv/static/static/font/fontello.1579102213354.woff2 b/priv/static/static/font/fontello.1579102213354.woff2 new file mode 100644 index 000000000..9c354e7f6 Binary files /dev/null and b/priv/static/static/font/fontello.1579102213354.woff2 differ diff --git a/priv/static/static/fontello.1576166651574.css b/priv/static/static/fontello.1579102213354.css similarity index 80% rename from priv/static/static/fontello.1576166651574.css rename to priv/static/static/fontello.1579102213354.css index 54f9fe05f..0f81954a5 100644 Binary files a/priv/static/static/fontello.1576166651574.css and b/priv/static/static/fontello.1579102213354.css differ diff --git a/priv/static/static/fontello.json b/priv/static/static/fontello.json index c0cf17271..829241b55 100755 --- a/priv/static/static/fontello.json +++ b/priv/static/static/fontello.json @@ -303,6 +303,42 @@ "css": "gauge", "code": 61668, "src": "fontawesome" + }, + { + "uid": "31972e4e9d080eaa796290349ae6c1fd", + "css": "users", + "code": 59421, + "src": "fontawesome" + }, + { + "uid": "e82cedfa1d5f15b00c5a81c9bd731ea2", + "css": "info-circled", + "code": 59423, + "src": "fontawesome" + }, + { + "uid": "w3nzesrlbezu6f30q7ytyq919p6gdlb6", + "css": "home-2", + "code": 59425, + "src": "typicons" + }, + { + "uid": "dcedf50ab1ede3283d7a6c70e2fe32f3", + "css": "chat", + "code": 59422, + "src": "fontawesome" + }, + { + "uid": "3a00327e61b997b58518bd43ed83c3df", + "css": "login", + "code": 59424, + "src": "fontawesome" + }, + { + "uid": "f3ebd6751c15a280af5cc5f4a764187d", + "css": "arrow-curved", + "code": 59426, + "src": "iconic" } ] } \ No newline at end of file diff --git a/priv/static/static/js/2.8896ea39a0ea8016391a.js b/priv/static/static/js/2.8896ea39a0ea8016391a.js new file mode 100644 index 000000000..ece883546 Binary files /dev/null and b/priv/static/static/js/2.8896ea39a0ea8016391a.js differ diff --git a/priv/static/static/js/2.8896ea39a0ea8016391a.js.map b/priv/static/static/js/2.8896ea39a0ea8016391a.js.map new file mode 100644 index 000000000..4a5dc5be7 Binary files /dev/null and b/priv/static/static/js/2.8896ea39a0ea8016391a.js.map differ diff --git a/priv/static/static/js/2.c96b30ae9f2d3f46f0ad.js b/priv/static/static/js/2.c96b30ae9f2d3f46f0ad.js deleted file mode 100644 index 910d304d3..000000000 Binary files a/priv/static/static/js/2.c96b30ae9f2d3f46f0ad.js and /dev/null differ diff --git a/priv/static/static/js/app.a43640742dacfb13b6b0.js b/priv/static/static/js/app.a43640742dacfb13b6b0.js new file mode 100644 index 000000000..82265996f Binary files /dev/null and b/priv/static/static/js/app.a43640742dacfb13b6b0.js differ diff --git a/priv/static/static/js/app.a43640742dacfb13b6b0.js.map b/priv/static/static/js/app.a43640742dacfb13b6b0.js.map new file mode 100644 index 000000000..b30f1ac4c Binary files /dev/null and b/priv/static/static/js/app.a43640742dacfb13b6b0.js.map differ diff --git a/priv/static/static/js/app.a9b3f4c3e79baf3fa8b7.js b/priv/static/static/js/app.a9b3f4c3e79baf3fa8b7.js deleted file mode 100644 index 124f284be..000000000 Binary files a/priv/static/static/js/app.a9b3f4c3e79baf3fa8b7.js and /dev/null differ diff --git a/priv/static/static/js/vendors~app.3f1ed7a4fdfc37ee27a7.js b/priv/static/static/js/vendors~app.3f1ed7a4fdfc37ee27a7.js deleted file mode 100644 index a64eee9a9..000000000 Binary files a/priv/static/static/js/vendors~app.3f1ed7a4fdfc37ee27a7.js and /dev/null differ diff --git a/priv/static/static/js/vendors~app.86bc6d5e06d2e17976c5.js b/priv/static/static/js/vendors~app.86bc6d5e06d2e17976c5.js new file mode 100644 index 000000000..0b8705ae8 Binary files /dev/null and b/priv/static/static/js/vendors~app.86bc6d5e06d2e17976c5.js differ diff --git a/priv/static/static/js/vendors~app.86bc6d5e06d2e17976c5.js.map b/priv/static/static/js/vendors~app.86bc6d5e06d2e17976c5.js.map new file mode 100644 index 000000000..98d62c3b1 Binary files /dev/null and b/priv/static/static/js/vendors~app.86bc6d5e06d2e17976c5.js.map differ diff --git a/priv/static/static/styles.json b/priv/static/static/styles.json index 842092c44..23508970d 100644 --- a/priv/static/static/styles.json +++ b/priv/static/static/styles.json @@ -1,6 +1,7 @@ { "pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], "pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], + "pleroma-amoled": [ "Pleroma Dark AMOLED", "#000000", "#111111", "#b0b0b1", "#d8a070", "#aa0000", "#0fa00f", "#0095ff", "#d59500"], "classic-dark": [ "Classic Dark", "#161c20", "#282e32", "#b9b9b9", "#baaa9c", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], "bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"], "ir-black": [ "Ir Black", "#000000", "#242422", "#b5b3aa", "#ff6c60", "#FF6C60", "#A8FF60", "#96CBFE", "#FFFFB6" ], diff --git a/priv/static/sw-pleroma.js b/priv/static/sw-pleroma.js index 4738f3391..ae01a067e 100644 Binary files a/priv/static/sw-pleroma.js and b/priv/static/sw-pleroma.js differ diff --git a/priv/static/sw-pleroma.js.map b/priv/static/sw-pleroma.js.map index 1ac8d8676..5d9874693 100644 Binary files a/priv/static/sw-pleroma.js.map and b/priv/static/sw-pleroma.js.map differ diff --git a/test/notification_test.exs b/test/notification_test.exs index f5f23bb5a..9a1c2f2b5 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -745,7 +745,7 @@ test "it doesn't return notifications from a blocked user when with_muted is set {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) - assert length(Notification.for_user(user, %{with_muted: true})) == 0 + assert Enum.empty?(Notification.for_user(user, %{with_muted: true})) end test "it doesn't return notifications from a domain-blocked user when with_muted is set" do @@ -755,7 +755,7 @@ test "it doesn't return notifications from a domain-blocked user when with_muted {:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"}) - assert length(Notification.for_user(user, %{with_muted: true})) == 0 + assert Enum.empty?(Notification.for_user(user, %{with_muted: true})) end test "it returns notifications from muted threads when with_muted is set" do diff --git a/test/object_test.exs b/test/object_test.exs index b002c2bae..9b4e6f0bf 100644 --- a/test/object_test.exs +++ b/test/object_test.exs @@ -4,12 +4,14 @@ defmodule Pleroma.ObjectTest do use Pleroma.DataCase + use Oban.Testing, repo: Pleroma.Repo import ExUnit.CaptureLog import Pleroma.Factory import Tesla.Mock alias Pleroma.Activity alias Pleroma.Object alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers alias Pleroma.Web.CommonAPI setup do @@ -99,6 +101,8 @@ test "in subdirectories" do Object.delete(note) + ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker)) + assert Object.get_by_id(attachment.id) == nil assert {:ok, []} == File.ls("#{uploads_dir}/#{path}") @@ -133,10 +137,46 @@ test "with dedupe enabled" do Object.delete(note) + ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker)) + assert Object.get_by_id(attachment.id) == nil assert {:ok, files} = File.ls(uploads_dir) refute filename in files end + + test "with objects that have legacy data.url attribute" do + Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + user = insert(:user) + + {:ok, %Object{} = attachment} = + Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id) + + {:ok, %Object{}} = Object.create(%{url: "https://google.com", actor: user.ap_id}) + + %{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} = + note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}}) + + uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads]) + + path = href |> Path.dirname() |> Path.basename() + + assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}") + + Object.delete(note) + + ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker)) + + assert Object.get_by_id(attachment.id) == nil + + assert {:ok, []} == File.ls("#{uploads_dir}/#{path}") + end end describe "normalizer" do diff --git a/test/repo_test.exs b/test/repo_test.exs index 85b64d4d1..5526b0327 100644 --- a/test/repo_test.exs +++ b/test/repo_test.exs @@ -4,7 +4,10 @@ defmodule Pleroma.RepoTest do use Pleroma.DataCase + import ExUnit.CaptureLog import Pleroma.Factory + import Mock + alias Pleroma.User describe "find_resource/1" do @@ -46,4 +49,44 @@ test "return error if has not assoc " do assert Repo.get_assoc(token, :user) == {:error, :not_found} end end + + describe "check_migrations_applied!" do + setup_with_mocks([ + {Ecto.Migrator, [], + [ + with_repo: fn repo, fun -> passthrough([repo, fun]) end, + migrations: fn Pleroma.Repo -> + [ + {:up, 20_191_128_153_944, "fix_missing_following_count"}, + {:up, 20_191_203_043_610, "create_report_notes"}, + {:down, 20_191_220_174_645, "add_scopes_to_pleroma_feo_auth_records"} + ] + end + ]} + ]) do + :ok + end + + test "raises if it detects unapplied migrations" do + assert_raise Pleroma.Repo.UnappliedMigrationsError, fn -> + capture_log(&Repo.check_migrations_applied!/0) + end + end + + test "doesn't do anything if disabled" do + disable_migration_check = + Pleroma.Config.get([:i_am_aware_this_may_cause_data_loss, :disable_migration_check]) + + Pleroma.Config.put([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true) + + on_exit(fn -> + Pleroma.Config.put( + [:i_am_aware_this_may_cause_data_loss, :disable_migration_check], + disable_migration_check + ) + end) + + assert :ok == Repo.check_migrations_applied!() + end + end end diff --git a/test/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/web/activity_pub/transmogrifier/follow_handling_test.exs index 7d6d0814d..1c88b05c2 100644 --- a/test/web/activity_pub/transmogrifier/follow_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/follow_handling_test.exs @@ -78,7 +78,7 @@ test "with locked accounts, it does not create a follow or an accept" do ) |> Repo.all() - assert length(accepts) == 0 + assert Enum.empty?(accepts) end test "it works for follow requests when you are already followed, creating a new accept activity" do diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index a3fbb6041..c8f8ba310 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -1363,6 +1363,30 @@ test "returns 404 when report id is invalid", %{conn: conn} do } end + test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do + read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"]) + write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"]) + + response = + conn + |> assign(:token, read_token) + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [%{"state" => "resolved", "id" => id}] + }) + |> json_response(403) + + assert response == %{ + "error" => "Insufficient permissions: admin:write:reports." + } + + conn + |> assign(:token, write_token) + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [%{"state" => "resolved", "id" => id}] + }) + |> json_response(:no_content) + end + test "mark report as resolved", %{conn: conn, id: id, admin: admin} do conn |> patch("/api/pleroma/admin/reports", %{ @@ -2840,7 +2864,7 @@ test "GET /instances/:instance/statuses", %{conn: conn} do response = json_response(ret_conn, 200) - assert length(response) == 0 + assert Enum.empty?(response) end end diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index b5d6d4055..f8963e42e 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -284,22 +284,22 @@ test "favoriting a status" do {:ok, %Activity{}, _} = CommonAPI.favorite(activity.id, user) end - test "retweeting a status twice returns an error" do + test "retweeting a status twice returns the status" do user = insert(:user) other_user = insert(:user) {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) - {:ok, %Activity{}, _object} = CommonAPI.repeat(activity.id, user) - {:error, _} = CommonAPI.repeat(activity.id, user) + {:ok, %Activity{} = activity, object} = CommonAPI.repeat(activity.id, user) + {:ok, ^activity, ^object} = CommonAPI.repeat(activity.id, user) end - test "favoriting a status twice returns an error" do + test "favoriting a status twice returns the status" do user = insert(:user) other_user = insert(:user) {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"}) - {:ok, %Activity{}, _object} = CommonAPI.favorite(activity.id, user) - {:error, _} = CommonAPI.favorite(activity.id, user) + {:ok, %Activity{} = activity, object} = CommonAPI.favorite(activity.id, user) + {:ok, ^activity, ^object} = CommonAPI.favorite(activity.id, user) end end diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs index 2588898d0..4b761e039 100644 --- a/test/web/common_api/common_api_utils_test.exs +++ b/test/web/common_api/common_api_utils_test.exs @@ -307,7 +307,7 @@ test "for private posts, not a reply" do {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "private", nil) assert length(to) == 2 - assert length(cc) == 0 + assert Enum.empty?(cc) assert mentioned_user.ap_id in to assert user.follower_address in to @@ -323,7 +323,7 @@ test "for private posts, a reply" do {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private", nil) assert length(to) == 3 - assert length(cc) == 0 + assert Enum.empty?(cc) assert mentioned_user.ap_id in to assert third_user.ap_id in to @@ -338,7 +338,7 @@ test "for direct posts, not a reply" do {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "direct", nil) assert length(to) == 1 - assert length(cc) == 0 + assert Enum.empty?(cc) assert mentioned_user.ap_id in to end @@ -353,7 +353,7 @@ test "for direct posts, a reply" do {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct", nil) assert length(to) == 2 - assert length(cc) == 0 + assert Enum.empty?(cc) assert mentioned_user.ap_id in to assert third_user.ap_id in to diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/web/mastodon_api/controllers/notification_controller_test.exs index 86303f92f..6f0606250 100644 --- a/test/web/mastodon_api/controllers/notification_controller_test.exs +++ b/test/web/mastodon_api/controllers/notification_controller_test.exs @@ -457,6 +457,30 @@ test "preserves parameters in link headers" do end end + describe "from specified user" do + test "account_id" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + + %{id: account_id} = other_user1 = insert(:user) + other_user2 = insert(:user) + + {:ok, _activity} = CommonAPI.post(other_user1, %{"status" => "hi @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(other_user2, %{"status" => "bye @#{user.nickname}"}) + + assert [%{"account" => %{"id" => ^account_id}}] = + conn + |> assign(:user, user) + |> get("/api/v1/notifications", %{account_id: account_id}) + |> json_response(200) + + assert %{"error" => "Account is not found"} = + conn + |> assign(:user, user) + |> get("/api/v1/notifications", %{account_id: "cofe"}) + |> json_response(404) + end + end + defp get_notification_id_by_activity(%{id: id}) do Notification |> Repo.get_by(activity_id: id) diff --git a/test/web/mastodon_api/controllers/search_controller_test.exs b/test/web/mastodon_api/controllers/search_controller_test.exs index 7fedf42e5..effae130c 100644 --- a/test/web/mastodon_api/controllers/search_controller_test.exs +++ b/test/web/mastodon_api/controllers/search_controller_test.exs @@ -53,7 +53,8 @@ test "search", %{conn: conn} do {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) results = - get(conn, "/api/v2/search", %{"q" => "2hu #private"}) + conn + |> get("/api/v2/search", %{"q" => "2hu #private"}) |> json_response(200) [account | _] = results["accounts"] @@ -73,6 +74,30 @@ test "search", %{conn: conn} do [status] = results["statuses"] assert status["id"] == to_string(activity.id) end + + test "excludes a blocked users from search results", %{conn: conn} do + user = insert(:user) + user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"}) + user_neo = insert(:user, %{nickname: "Agent Neo", name: "Agent"}) + + {:ok, act1} = CommonAPI.post(user, %{"status" => "This is about 2hu private 天子"}) + {:ok, act2} = CommonAPI.post(user_smith, %{"status" => "Agent Smith"}) + {:ok, act3} = CommonAPI.post(user_neo, %{"status" => "Agent Smith"}) + Pleroma.User.block(user, user_smith) + + results = + conn + |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) + |> get("/api/v2/search", %{"q" => "Agent"}) + |> json_response(200) + + status_ids = Enum.map(results["statuses"], fn g -> g["id"] end) + + assert act3.id in status_ids + refute act2.id in status_ids + refute act1.id in status_ids + end end describe ".account_search" do @@ -146,11 +171,10 @@ test "search", %{conn: conn} do {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) - conn = + results = conn |> get("/api/v1/search", %{"q" => "2hu"}) - - assert results = json_response(conn, 200) + |> json_response(200) [account | _] = results["accounts"] assert account["id"] == to_string(user_three.id) @@ -168,11 +192,10 @@ test "search fetches remote statuses and prefers them over other results", %{con "status" => "check out https://shitposter.club/notice/2827873" }) - conn = + results = conn |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"}) - - assert results = json_response(conn, 200) + |> json_response(200) [status, %{"id" => ^activity_id}] = results["statuses"] @@ -189,11 +212,10 @@ test "search doesn't show statuses that it shouldn't", %{conn: conn} do }) capture_log(fn -> - conn = + results = conn |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]}) - - assert results = json_response(conn, 200) + |> json_response(200) [] = results["statuses"] end) @@ -202,23 +224,23 @@ test "search doesn't show statuses that it shouldn't", %{conn: conn} do test "search fetches remote accounts", %{conn: conn} do user = insert(:user) - conn = + results = conn |> assign(:user, user) |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) |> get("/api/v1/search", %{"q" => "mike@osada.macgirvin.com", "resolve" => "true"}) + |> json_response(200) - assert results = json_response(conn, 200) [account] = results["accounts"] assert account["acct"] == "mike@osada.macgirvin.com" end test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do - conn = + results = conn |> get("/api/v1/search", %{"q" => "mike@osada.macgirvin.com", "resolve" => "false"}) + |> json_response(200) - assert results = json_response(conn, 200) assert [] == results["accounts"] end diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index 307221c5d..b03b4b344 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -638,6 +638,13 @@ test "favs a status and returns it", %{conn: conn} do assert to_string(activity.id) == id end + test "favoriting twice will just return 200", %{conn: conn} do + activity = insert(:note_activity) + + post(conn, "/api/v1/statuses/#{activity.id}/favourite") + assert post(conn, "/api/v1/statuses/#{activity.id}/favourite") |> json_response(200) + end + test "returns 400 error for a wrong id", %{conn: conn} do conn = post(conn, "/api/v1/statuses/1/favourite") diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs index 17b6ebcbc..b54b19c0b 100644 --- a/test/web/mastodon_api/views/status_view_test.exs +++ b/test/web/mastodon_api/views/status_view_test.exs @@ -24,6 +24,22 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do :ok end + test "has an emoji reaction list" do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "dae cofe??"}) + + {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, user, "☕") + {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵") + activity = Repo.get(Activity, activity.id) + status = StatusView.render("show.json", activity: activity) + + assert status[:pleroma][:emoji_reactions]["🍵"] == 1 + assert status[:pleroma][:emoji_reactions]["☕"] == 2 + end + test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do user = insert(:user) @@ -172,7 +188,8 @@ test "a note activity" do spoiler_text: %{"text/plain" => HTML.strip_tags(object_data["summary"])}, expires_at: nil, direct_conversation_id: nil, - thread_muted: false + thread_muted: false, + emoji_reactions: %{} } } diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index 9cc534f57..59f4674eb 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -568,29 +568,34 @@ test "with existing authentication and OOB `redirect_uri`, redirects to app with describe "POST /oauth/authorize" do test "redirects with oauth authorization, " <> - "keeping only non-admin scopes for non-admin user" do - app = insert(:oauth_app, scopes: ["read", "write", "admin"]) + "granting requested app-supported scopes to both admin- and non-admin users" do + app_scopes = ["read", "write", "admin", "secret_scope"] + app = insert(:oauth_app, scopes: app_scopes) redirect_uri = OAuthController.default_redirect_uri(app) non_admin = insert(:user, is_admin: false) admin = insert(:user, is_admin: true) + scopes_subset = ["read:subscope", "write", "admin"] - for {user, expected_scopes} <- %{ - non_admin => ["read:subscope", "write"], - admin => ["read:subscope", "write", "admin"] - } do + # In case scope param is missing, expecting _all_ app-supported scopes to be granted + for user <- [non_admin, admin], + {requested_scopes, expected_scopes} <- + %{scopes_subset => scopes_subset, nil => app_scopes} do conn = - build_conn() - |> post("/oauth/authorize", %{ - "authorization" => %{ - "name" => user.nickname, - "password" => "test", - "client_id" => app.client_id, - "redirect_uri" => redirect_uri, - "scope" => "read:subscope write admin", - "state" => "statepassed" + post( + build_conn(), + "/oauth/authorize", + %{ + "authorization" => %{ + "name" => user.nickname, + "password" => "test", + "client_id" => app.client_id, + "redirect_uri" => redirect_uri, + "scope" => requested_scopes, + "state" => "statepassed" + } } - }) + ) target = redirected_to(conn) assert target =~ redirect_uri @@ -631,34 +636,31 @@ test "returns 401 for wrong credentials", %{conn: conn} do assert result =~ "Invalid Username/Password" end - test "returns 401 for missing scopes " <> - "(including all admin-only scopes for non-admin user)" do + test "returns 401 for missing scopes" do user = insert(:user, is_admin: false) app = insert(:oauth_app, scopes: ["read", "write", "admin"]) redirect_uri = OAuthController.default_redirect_uri(app) - for scope_param <- ["", "admin:read admin:write"] do - result = - build_conn() - |> post("/oauth/authorize", %{ - "authorization" => %{ - "name" => user.nickname, - "password" => "test", - "client_id" => app.client_id, - "redirect_uri" => redirect_uri, - "state" => "statepassed", - "scope" => scope_param - } - }) - |> html_response(:unauthorized) + result = + build_conn() + |> post("/oauth/authorize", %{ + "authorization" => %{ + "name" => user.nickname, + "password" => "test", + "client_id" => app.client_id, + "redirect_uri" => redirect_uri, + "state" => "statepassed", + "scope" => "" + } + }) + |> html_response(:unauthorized) - # Keep the details - assert result =~ app.client_id - assert result =~ redirect_uri + # Keep the details + assert result =~ app.client_id + assert result =~ redirect_uri - # Error message - assert result =~ "This action is outside the authorized scopes" - end + # Error message + assert result =~ "This action is outside the authorized scopes" end test "returns 401 for scopes beyond app scopes hierarchy", %{conn: conn} do diff --git a/test/web/pleroma_api/controllers/emoji_api_controller_test.exs b/test/web/pleroma_api/controllers/emoji_api_controller_test.exs index e1b484dae..8e76f2f3d 100644 --- a/test/web/pleroma_api/controllers/emoji_api_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_api_controller_test.exs @@ -14,6 +14,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do "emoji" ) + clear_config([:auth, :enforce_oauth_admin_scope_usage]) do + Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false) + end + test "shared & non-shared pack information in list_packs is ok" do conn = build_conn() resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) 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 3f7ef13bc..fb7500134 100644 --- a/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs +++ b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs @@ -57,11 +57,6 @@ test "GET /api/v1/pleroma/statuses/:id/emoji_reactions_by", %{conn: conn} do {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) - conn = - conn - |> assign(:user, user) - |> assign(:token, insert(:oauth_token, user: user, scopes: ["read:statuses"])) - result = conn |> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by") diff --git a/test/web/twitter_api/password_controller_test.exs b/test/web/twitter_api/password_controller_test.exs index 840c84a05..29ba7d265 100644 --- a/test/web/twitter_api/password_controller_test.exs +++ b/test/web/twitter_api/password_controller_test.exs @@ -55,7 +55,7 @@ test "it returns HTTP 200", %{conn: conn} do user = refresh_record(user) assert Comeonin.Pbkdf2.checkpw("test", user.password_hash) - assert length(Token.get_user_tokens(user)) == 0 + assert Enum.empty?(Token.get_user_tokens(user)) end test "it sets password_reset_pending to false", %{conn: conn} do