diff --git a/.formatter.exs b/.formatter.exs index 5799ac127..abd91dbbe 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,3 +1,3 @@ [ - inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}", "priv/repo/migrations/*.exs", "priv/scrubbers/*.ex"] + inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}", "priv/repo/migrations/*.exs", "priv/repo/optional_migrations/**/*.exs", "priv/scrubbers/*.ex"] ] diff --git a/.gitignore b/.gitignore index 6ae21e914..f30f4cf5f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /db /deps /*.ez +/test/instance /test/uploads /.elixir_ls /test/fixtures/DSCN0010_tmp.jpg @@ -31,7 +32,7 @@ erl_crash.dump # Database setup file, some may forget to delete it -/config/setup_db.psql +/config/setup_db*.psql .DS_Store .env @@ -52,3 +53,7 @@ pleroma.iml # asdf .tool-versions + +# Editor temp files +/*~ +/*# \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9ef3ddd0d..78e715d47 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,7 +8,9 @@ variables: &global_variables MIX_ENV: test cache: &global_cache_policy - key: ${CI_COMMIT_REF_SLUG} + key: + files: + - mix.lock paths: - deps - _build @@ -22,18 +24,30 @@ stages: - docker before_script: + - rm -rf _build/*/lib/pleroma - apt-get update && apt-get install -y cmake - mix local.hex --force - mix local.rebar --force + - mix deps.get - apt-get -qq update - apt-get install -y libmagic-dev +after_script: + - rm -rf _build/*/lib/pleroma + build: stage: build script: - - mix deps.get - mix compile --force +spec-build: + stage: test + artifacts: + paths: + - spec.json + script: + - mix pleroma.openapi_spec spec.json + benchmark: stage: benchmark when: manual @@ -44,7 +58,6 @@ benchmark: alias: postgres command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] script: - - mix deps.get - mix ecto.create - mix ecto.migrate - mix pleroma.load_testing @@ -62,7 +75,6 @@ unit-testing: command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] script: - apt-get update && apt-get install -y libimage-exiftool-perl ffmpeg - - mix deps.get - mix ecto.create - mix ecto.migrate - mix coveralls --preload-modules @@ -96,7 +108,6 @@ unit-testing-rum: RUM_ENABLED: "true" script: - apt-get update && apt-get install -y libimage-exiftool-perl ffmpeg - - mix deps.get - mix ecto.create - mix ecto.migrate - "mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/" @@ -112,7 +123,6 @@ analysis: stage: test cache: *testing_cache_policy script: - - mix deps.get - mix credo --strict --only=warnings,todo,fixme,consistency,readability docs-deploy: @@ -155,6 +165,20 @@ review_app: - (ssh -t dokku@pleroma.online -- certs:add "$CI_ENVIRONMENT_SLUG" /home/dokku/server.crt /home/dokku/server.key) || true - git push -f dokku@pleroma.online:$CI_ENVIRONMENT_SLUG $CI_COMMIT_SHA:refs/heads/master +spec-deploy: + stage: deploy + artifacts: + paths: + - spec.json + only: + - develop@pleroma/pleroma + image: alpine:latest + before_script: + - apk add curl + script: + - curl -X POST -F"token=$API_DOCS_PIPELINE_TRIGGER" -F'ref=master' -F"variables[BRANCH]=$CI_COMMIT_REF_NAME" -F"variables[JOB_REF]=$CI_JOB_ID" https://git.pleroma.social/api/v4/projects/1130/trigger/pipeline + + stop_review_app: image: alpine:3.9 stage: deploy @@ -213,7 +237,7 @@ amd64-musl: stage: release artifacts: *release-artifacts only: *release-only - image: elixir:1.10.3-alpine + image: elixir:1.10.3-alpine cache: *release-cache variables: *release-variables before_script: &before-release-musl @@ -349,3 +373,26 @@ docker-release: - dind only: - /^release/.*$/@pleroma/pleroma + +docker-adhoc: + stage: docker + image: docker:latest + cache: {} + dependencies: [] + variables: *docker-variables + before_script: *before-docker + allow_failure: true + script: + script: + - mkdir -p /root/.docker/cli-plugins + - wget "${DOCKER_BUILDX_URL}" -O ~/.docker/cli-plugins/docker-buildx + - echo "${DOCKER_BUILDX_HASH} /root/.docker/cli-plugins/docker-buildx" | sha1sum -c + - chmod +x ~/.docker/cli-plugins/docker-buildx + - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + - docker buildx create --name mbuilder --driver docker-container --use + - docker buildx inspect --bootstrap + - docker buildx build --platform linux/amd64,linux/arm/v7,linux/arm64/v8 --push --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 . + tags: + - dind + only: + - /^build-docker/.*$/@pleroma/pleroma diff --git a/.mailmap b/.mailmap index e4ca5f9b5..84fffaee7 100644 --- a/.mailmap +++ b/.mailmap @@ -1,2 +1,3 @@ Ariadne Conill Ariadne Conill +rinpatch diff --git a/CHANGELOG.md b/CHANGELOG.md index 919c5a102..9a0171763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,65 +8,160 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed +- The `application` metadata returned with statuses is no longer hardcoded. Apps that want to display these details will now have valid data for new posts after this change. +- HTTPSecurityPlug now sends a response header to opt out of Google's FLoC (Federated Learning of Cohorts) targeted advertising. + +### Added + +- MRF (`FollowBotPolicy`): New MRF Policy which makes a designated local Bot account attempt to follow all users in public Notes received by your instance. Users who require approving follower requests or have #nobot in their profile are excluded. +- Return OAuth token `id` (primary key) in POST `/oauth/token`. + +## Unreleased (Patch) + +### Fixed + +- Try to save exported ConfigDB settings (migrate_from_db) in the system temp directory if default location is not writable. +- Uploading custom instance thumbnail via AdminAPI/AdminFE generated invalid URL to the image +- Applying ConcurrentLimiter settings via AdminAPI +- User login failures if their `notification_settings` were in a NULL state. +- Mix task `pleroma.user delete_activities` query transaction timeout is now :infinity + +## [2.3.0] - 2020-03-01 + +### Security + +- Fixed client user agent leaking through MediaProxy + +### Removed + +- `:auth, :enforce_oauth_admin_scope_usage` configuration option. + +### Changed + +- **Breaking**: Changed `mix pleroma.user toggle_confirmed` to `mix pleroma.user confirm` +- **Breaking**: Changed `mix pleroma.user toggle_activated` to `mix pleroma.user activate/deactivate` +- **Breaking:** NSFW hashtag is no longer added on sensitive posts - Polls now always return a `voters_count`, even if they are single-choice. - Admin Emails: The ap id is used as the user link in emails now. +- Improved registration workflow for email confirmation and account approval modes. - Search: When using Postgres 11+, Pleroma will use the `websearch_to_tsvector` function to parse search queries. - Emoji: Support the full Unicode 13.1 set of Emoji for reactions, plus regional indicators. +- Deprecated `Pleroma.Uploaders.S3, :public_endpoint`. Now `Pleroma.Upload, :base_url` is the standard configuration key for all uploaders. +- Improved Apache webserver support: updated sample configuration, MediaProxy cache invalidation verified with the included sample script +- Improve OAuth 2.0 provider support. A missing `fqn` field was added to the response, but does not expose the user's email address. +- Provide redirect of external posts from `/notice/:id` to their original URL +- Admins no longer receive notifications for reports if they are the actor making the report. +- Improved Mailer configuration setting descriptions for AdminFE. +- Updated default avatar to look nicer. + +
+ API Changes + +- **Breaking:** AdminAPI changed User field `confirmation_pending` to `is_confirmed` +- **Breaking:** AdminAPI changed User field `approval_pending` to `is_approved` +- **Breaking**: AdminAPI changed User field `deactivated` to `is_active` +- **Breaking:** AdminAPI `GET /api/pleroma/admin/users/:nickname_or_id/statuses` changed response format and added the number of total users posts. +- **Breaking:** AdminAPI `GET /api/pleroma/admin/instances/:instance/statuses` changed response format and added the number of total users posts. +- Admin API: Reports now ordered by newest +- Pleroma API: `GET /api/v1/pleroma/chats` is deprecated in favor of `GET /api/v2/pleroma/chats`. +- Pleroma API: Reroute `/api/pleroma/*` to `/api/v1/pleroma/*` + +
+- Improved hashtag timeline performance (requires a background migration). ### Added - Reports now generate notifications for admins and mods. -- Experimental websocket-based federation between Pleroma instances. -- Support for local-only statuses +- Support for local-only statuses. - Support pagination of blocks and mutes. - Account backup. - Configuration: Add `:instance, autofollowing_nicknames` setting to provide a way to make accounts automatically follow new users that register on the local Pleroma instance. - Ability to view remote timelines, with ex. `/api/v1/timelines/public?instance=lain.com` and streams `public:remote` and `public:remote:media`. - The site title is now injected as a `title` tag like preloads or metadata. - Password reset tokens now are not accepted after a certain age. -- Mix tasks to help with displaying and removing ConfigDB entries. See `mix pleroma.config` +- Mix tasks to help with displaying and removing ConfigDB entries. See `mix pleroma.config`. - OAuth form improvements: users are remembered by their cookie, the CSS is overridable by the admin, and the style has been improved. -- OAuth improvements and fixes: more secure session-based authentication (by token that could be revoked anytime), ability to revoke belonging OAuth token from any client etc. +- OAuth improvements and fixes: more secure session-based authentication (by token that could be revoked anytime), ability to revoke belonging OAuth token from any client etc. +- Ability to set ActivityPub aliases for follower migration. +- Configurable background job limits for RichMedia (link previews) and MediaProxyWarmingPolicy +- Ability to define custom HTTP headers per each frontend +- MRF (`NoEmptyPolicy`): New MRF Policy which will deny empty statuses or statuses of only mentions from being created by local users +- New users will receive a simple email confirming their registration if no other emails will be dispatched. (e.g., Welcome, Confirmation, or Approval Required)
API Changes - Admin API: (`GET /api/pleroma/admin/users`) filter users by `unconfirmed` status and `actor_type`. +- Admin API: OpenAPI spec for the user-related operations +- Pleroma API: `GET /api/v2/pleroma/chats` added. It is exactly like `GET /api/v1/pleroma/chats` except supports pagination. - Pleroma API: Add `idempotency_key` to the chat message entity that can be used for optimistic message sending. - Pleroma API: (`GET /api/v1/pleroma/federation_status`) Add a way to get a list of unreachable instances. - Mastodon API: User and conversation mutes can now auto-expire if `expires_in` parameter was given while adding the mute. -- Admin API: An endpoint to manage frontends -- Streaming API: Add follow relationships updates +- Admin API: An endpoint to manage frontends. +- Streaming API: Add follow relationships updates. +- WebPush: Introduce `pleroma:chat_mention` and `pleroma:emoji_reaction` notification types. +- Mastodon API: Add monthly active users to `/api/v1/instance` (`pleroma.stats.mau`). +- Mastodon API: Home, public, hashtag & list timelines accept `only_media`, `remote` & `local` parameters for filtration. +- Mastodon API: `/api/v1/accounts/:id` & `/api/v1/mutes` endpoints accept `with_relationships` parameter and return filled `pleroma.relationship` field. +- Mastodon API: Endpoint to remove a conversation (`DELETE /api/v1/conversations/:id`). +- Mastodon API: `expires_in` in the scheduled post `params` field on `/api/v1/statuses` and `/api/v1/scheduled_statuses/:id` endpoints.
### Fixed - Users with `is_discoverable` field set to false (default value) will appear in in-service search results but be hidden from external services (search bots etc.). +- Streaming API: Posts and notifications are not dropped, when CLI task is executing. +- Creating incorrect IPv4 address-style HTTP links when encountering certain numbers. +- Reblog API Endpoint: Do not set visibility parameter to public by default and let CommonAPI to infer it from status, so a user can reblog their private status without explicitly setting reblog visibility to private. +- Tag URLs in statuses are now absolute +- Removed duplicate jobs to purge expired activities +- File extensions of some attachments were incorrectly changed. This feature has been disabled for now. +- Mix task pleroma.instance creates missing parent directories if the configuration or SQL output paths are changed.
API Changes - Mastodon API: Current user is now included in conversation if it's the only participant. - Mastodon API: Fixed last_status.account being not filled with account data. + - Mastodon API: Fix not being able to add or remove multiple users at once in lists. + - Mastodon API: Fixed own_votes being not returned with poll data. + - Mastodon API: Fixed creation of scheduled posts with polls. + - Mastodon API: Support for expires_in/expires_at in the Filters.
-## Unreleased (Patch) +## [2.2.2] - 2020-01-18 + +### Fixed + +- StealEmojiPolicy creates dir for emojis, if it doesn't exist. +- Updated `elixir_make` to a non-retired version + +### Upgrade notes + +1. Restart Pleroma + +## [2.2.1] - 2020-12-22 ### Changed +- Updated Pleroma FE ### Fixed - Config generation: rename `Pleroma.Upload.Filter.ExifTool` to `Pleroma.Upload.Filter.Exiftool`. -- Search: RUM index search speed has been fixed. - S3 Uploads with Elixir 1.11. -- Emoji Reaction activity filtering from blocked and muted accounts. - Mix task pleroma.user delete_activities for source installations. -- Fix ability to update Pleroma Chat push notifications with PUT /api/v1/push/subscription and alert type pleroma:chat_mention +- Search: RUM index search speed has been fixed. +- Rich Media Previews sometimes showed the wrong preview due to a bug following redirects. +- Fixes for the autolinker. - Forwarded reports duplication from Pleroma instances. +- Emoji Reaction activity filtering from blocked and muted accounts. -
- API -- Statuses were not displayed for Mastodon forwarded reports. +-
+ API + - Statuses were not displayed for Mastodon forwarded reports. +
-
+### Upgrade notes + +1. Restart Pleroma ## [2.2.0] - 2020-11-12 @@ -104,7 +199,7 @@ switched to a new configuration mechanism, however it was not officially removed - Media preview proxy (requires `ffmpeg` and `ImageMagick` to be installed and media proxy to be enabled; see `:media_preview_proxy` config for more details). - Mix tasks for controlling user account confirmation status in bulk (`mix pleroma.user confirm_all` and `mix pleroma.user unconfirm_all`) -- Mix task for sending confirmation emails to all unconfirmed users (`mix pleroma.email send_confirmation_mails`) +- Mix task for sending confirmation emails to all unconfirmed users (`mix pleroma.email resend_confirmation_emails`) - Mix task option for force-unfollowing relays - App metrics: ability to restrict access to specified IP whitelist. @@ -428,7 +523,6 @@ switched to a new configuration mechanism, however it was not officially removed - Static-FE: Fix remote posts not being sanitized ### Fixed -======= - Rate limiter crashes when there is no explicitly specified ip in the config - 500 errors when no `Accept` header is present if Static-FE is enabled - Instance panel not being updated immediately due to wrong `Cache-Control` headers diff --git a/COPYING b/COPYING index 3140c8038..dd25f1d81 100644 --- a/COPYING +++ b/COPYING @@ -1,10 +1,17 @@ -Unless otherwise stated this repository is copyright © 2017-2020 +Unless otherwise stated this repository is copyright © 2017-2021 Pleroma Authors , and is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as AGPL-3. --- +Files inside docs directory are copyright © 2021 Pleroma Authors +, and are distributed under the Creative Commons +Attribution 4.0 International license, you should have received +a copy of the license file as CC-BY-4.0. + +--- + The following files are copyright © 2019 shitposter.club, and are distributed under the Creative Commons Attribution-ShareAlike 4.0 International license, you should have received a copy of the license file as CC-BY-SA-4.0. diff --git a/benchmarks/load_testing/fetcher.ex b/benchmarks/load_testing/fetcher.ex index dfbd916be..607b7d4cb 100644 --- a/benchmarks/load_testing/fetcher.ex +++ b/benchmarks/load_testing/fetcher.ex @@ -33,10 +33,11 @@ defp fetch_user(user) do end defp create_filter(user) do - Pleroma.Filter.create(%Pleroma.Filter{ + Pleroma.Filter.create(%{ user_id: user.id, phrase: "must be filtered", - hide: true + hide: true, + context: ["home"] }) end diff --git a/benchmarks/load_testing/users.ex b/benchmarks/load_testing/users.ex index 34a904ac2..0a33cbfdb 100644 --- a/benchmarks/load_testing/users.ex +++ b/benchmarks/load_testing/users.ex @@ -55,7 +55,7 @@ defp generate_user(i) do name: "Test テスト User #{i}", email: "user#{i}@example.com", nickname: "nick#{i}", - password_hash: Pbkdf2.hash_pwd_salt("test"), + password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"), bio: "Tester Number #{i}", local: !remote } diff --git a/config/config.exs b/config/config.exs index c7ac0d22c..4381068ac 100644 --- a/config/config.exs +++ b/config/config.exs @@ -47,7 +47,6 @@ config :pleroma, ecto_repos: [Pleroma.Repo] config :pleroma, Pleroma.Repo, - types: Pleroma.PostgresTypes, telemetry_event: [Pleroma.Repo.Instrumenter], migration_lock: nil @@ -64,23 +63,24 @@ filters: [Pleroma.Upload.Filter.Dedupe], link_name: false, proxy_remote: false, - proxy_opts: [ - redirect_on_failure: false, - max_body_length: 25 * 1_048_576, - http: [ - follow_redirect: true, - pool: :upload - ] - ], filename_display_max_length: 30, - default_description: nil + default_description: nil, + base_url: nil config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads" config :pleroma, Pleroma.Uploaders.S3, bucket: nil, - streaming_enabled: true, - public_endpoint: "https://s3.amazonaws.com" + bucket_namespace: nil, + truncated_namespace: nil, + streaming_enabled: true + +config :ex_aws, :s3, + # host: "s3.wasabisys.com", # required if not Amazon AWS + access_key_id: nil, + secret_access_key: nil, + # region: "us-east-1", # may be required for Amazon AWS + scheme: "https://" config :pleroma, :emoji, shortcode_globs: ["/emoji/custom/**/*.png"], @@ -306,7 +306,7 @@ hideSitename: false, hideUserStats: false, loginMethod: "password", - logo: "/static/logo.png", + logo: "/static/logo.svg", logoMargin: ".1em", logoMask: true, minimalScopesMode: false, @@ -343,8 +343,8 @@ config :pleroma, :manifest, icons: [ %{ - src: "/static/logo.png", - type: "image/png" + src: "/static/logo.svg", + type: "image/svg+xml" } ], theme_color: "#282c37", @@ -391,6 +391,11 @@ federated_timeline_removal: [], replace: [] +config :pleroma, :mrf_hashtag, + sensitive: ["nsfw"], + reject: [], + federated_timeline_removal: [] + config :pleroma, :mrf_subchain, match_actor: %{} config :pleroma, :mrf_activity_expiration, days: 365 @@ -404,6 +409,8 @@ threshold: 604_800, actions: [:delist, :strip_followers] +config :pleroma, :mrf_follow_bot, follower_nickname: nil + config :pleroma, :rich_media, enabled: true, ignore_hosts: [], @@ -438,7 +445,9 @@ headers: [], options: [] -config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Script, script_path: nil +config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Script, + script_path: nil, + url_format: nil # Note: media preview proxy depends on media proxy to be enabled config :pleroma, :media_preview_proxy, @@ -541,6 +550,7 @@ queues: [ activity_expiration: 10, token_expiration: 5, + filter_expiration: 1, backup: 1, federator_incoming: 50, federator_outgoing: 50, @@ -551,7 +561,7 @@ scheduled_activities: 10, background: 5, remote_fetcher: 2, - attachments_cleanup: 5, + attachments_cleanup: 1, new_users_digest: 1, mute_expire: 5 ], @@ -608,10 +618,7 @@ base_path: "/oauth", providers: ueberauth_providers -config :pleroma, - :auth, - enforce_oauth_admin_scope_usage: true, - oauth_consumer_strategies: oauth_consumer_strategies +config :pleroma, :auth, oauth_consumer_strategies: oauth_consumer_strategies config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail, enabled: false @@ -648,12 +655,16 @@ } config :pleroma, :oauth2, - token_expires_in: 3600 * 24 * 30, + token_expires_in: 3600 * 24 * 365 * 100, issue_new_refresh_token: true, clean_expired_tokens: false config :pleroma, :database, rum_enabled: false +config :pleroma, :features, improved_hashtag_timeline: :auto + +config :pleroma, :populate_hashtags_table, fault_rate_allowance: 0.01 + config :pleroma, :env, Mix.env() config :http_signatures, @@ -723,7 +734,10 @@ "git" => "https://git.pleroma.social/pleroma/fedi-fe", "build_url" => "https://git.pleroma.social/pleroma/fedi-fe/-/jobs/artifacts/${ref}/download?job=build", - "ref" => "master" + "ref" => "master", + "custom-http-headers" => [ + {"service-worker-allowed", "/"} + ] }, "admin-fe" => %{ "name" => "admin-fe", @@ -832,6 +846,11 @@ limit_days: 7, dir: nil +config :pleroma, ConcurrentLimiter, [ + {Pleroma.Web.RichMedia.Helpers, [max_running: 5, max_waiting: 5]}, + {Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, [max_running: 5, max_waiting: 5]} +] + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{Mix.env()}.exs" diff --git a/config/description.exs b/config/description.exs index f4b8768da..bb1f43305 100644 --- a/config/description.exs +++ b/config/description.exs @@ -60,6 +60,12 @@ label: "Build directory", type: :string, description: "The directory inside the zip file " + }, + %{ + key: "custom-http-headers", + label: "Custom HTTP headers", + type: {:list, :string}, + description: "The custom HTTP headers for the frontend" } ] @@ -93,7 +99,8 @@ key: :base_url, label: "Base URL", type: :string, - description: "Base URL for the uploads, needed if you use CDN", + description: + "Base URL for the uploads. Required if you use a CDN or host attachments under a different domain.", suggestions: [ "https://cdn-host.com" ] @@ -101,74 +108,10 @@ %{ key: :proxy_remote, type: :boolean, - description: - "If enabled, requests to media stored using a remote uploader will be proxied instead of being redirected" - }, - %{ - key: :proxy_opts, - label: "Proxy Options", - type: :keyword, - description: "Options for Pleroma.ReverseProxy", - suggestions: [ - redirect_on_failure: false, - max_body_length: 25 * 1_048_576, - http: [ - follow_redirect: true, - pool: :media - ] - ], - children: [ - %{ - key: :redirect_on_failure, - type: :boolean, - description: - "Redirects the client to the real remote URL if there's any HTTP errors. " <> - "Any error during body processing will not be redirected as the response is chunked." - }, - %{ - key: :max_body_length, - type: :integer, - description: - "Limits the content length to be approximately the " <> - "specified length. It is validated with the `content-length` header and also verified when proxying." - }, - %{ - key: :http, - label: "HTTP", - type: :keyword, - description: "HTTP options", - children: [ - %{ - key: :adapter, - type: :keyword, - description: "Adapter specific options", - children: [ - %{ - key: :ssl_options, - type: :keyword, - label: "SSL Options", - description: "SSL options for HTTP adapter", - children: [ - %{ - key: :versions, - type: {:list, :atom}, - description: "List of TLS versions to use", - suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"] - } - ] - } - ] - }, - %{ - key: :proxy_url, - label: "Proxy URL", - type: [:string, :tuple], - description: "Proxy URL", - suggestions: ["127.0.0.1:8123", {:socks5, :localhost, 9050}] - } - ] - } - ] + description: """ + Proxy requests to the remote uploader.\n + Useful if media upload endpoint is not internet accessible. + """ }, %{ key: :filename_display_max_length, @@ -213,18 +156,12 @@ description: "S3 bucket namespace", suggestions: ["pleroma"] }, - %{ - key: :public_endpoint, - type: :string, - description: "S3 endpoint", - suggestions: ["https://s3.amazonaws.com"] - }, %{ key: :truncated_namespace, type: :string, description: "If you use S3 compatible service such as Digital Ocean Spaces or CDN, set folder name or \"\" etc." <> - " For example, when using CDN to S3 virtual host format, set \"\". At this time, write CNAME to CDN in public_endpoint." + " For example, when using CDN to S3 virtual host format, set \"\". At this time, write CNAME to CDN in Upload base_url." }, %{ key: :streaming_enabled, @@ -278,253 +215,216 @@ type: :group, description: "Mailer-related settings", children: [ + %{ + key: :enabled, + label: "Mailer Enabled", + type: :boolean + }, %{ key: :adapter, type: :module, description: - "One of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters)," <> - " or Swoosh.Adapters.Local for in-memory mailbox", + "One of the mail adapters listed in [Swoosh documentation](https://hexdocs.pm/swoosh/Swoosh.html#module-adapters)", suggestions: [ + Swoosh.Adapters.AmazonSES, + Swoosh.Adapters.Dyn, + Swoosh.Adapters.Gmail, + Swoosh.Adapters.Mailgun, + Swoosh.Adapters.Mailjet, + Swoosh.Adapters.Mandrill, + Swoosh.Adapters.Postmark, Swoosh.Adapters.SMTP, Swoosh.Adapters.Sendgrid, Swoosh.Adapters.Sendmail, - Swoosh.Adapters.Mandrill, - Swoosh.Adapters.Mailgun, - Swoosh.Adapters.Mailjet, - Swoosh.Adapters.Postmark, - Swoosh.Adapters.SparkPost, - Swoosh.Adapters.AmazonSES, - Swoosh.Adapters.Dyn, Swoosh.Adapters.SocketLabs, - Swoosh.Adapters.Gmail, - Swoosh.Adapters.Local + Swoosh.Adapters.SparkPost ] }, - %{ - key: :enabled, - type: :boolean, - description: "Allow/disallow send emails" - }, %{ group: {:subgroup, Swoosh.Adapters.SMTP}, key: :relay, type: :string, - description: "`Swoosh.Adapters.SMTP` adapter specific setting", - suggestions: ["smtp.gmail.com"] - }, - %{ - group: {:subgroup, Swoosh.Adapters.SMTP}, - key: :username, - type: :string, - description: "`Swoosh.Adapters.SMTP` adapter specific setting", - suggestions: ["pleroma"] - }, - %{ - group: {:subgroup, Swoosh.Adapters.SMTP}, - key: :password, - type: :string, - description: "`Swoosh.Adapters.SMTP` adapter specific setting", - suggestions: ["password"] - }, - %{ - group: {:subgroup, Swoosh.Adapters.SMTP}, - key: :ssl, - label: "SSL", - type: :boolean, - description: "`Swoosh.Adapters.SMTP` adapter specific setting" - }, - %{ - group: {:subgroup, Swoosh.Adapters.SMTP}, - key: :tls, - label: "TLS", - type: :atom, - description: "`Swoosh.Adapters.SMTP` adapter specific setting", - suggestions: [:always, :never, :if_available] - }, - %{ - group: {:subgroup, Swoosh.Adapters.SMTP}, - key: :auth, - type: :atom, - description: "`Swoosh.Adapters.SMTP` adapter specific setting", - suggestions: [:always, :never, :if_available] + description: "Hostname or IP address", + suggestions: ["smtp.example.com"] }, %{ group: {:subgroup, Swoosh.Adapters.SMTP}, key: :port, type: :integer, - description: "`Swoosh.Adapters.SMTP` adapter specific setting", - suggestions: [1025] + description: "SMTP port", + suggestions: ["1025"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :username, + type: :string, + description: "SMTP AUTH username", + suggestions: ["user@example.com"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :password, + type: :string, + description: "SMTP AUTH password", + suggestions: ["password"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :ssl, + label: "Use SSL", + type: :boolean, + description: "Use Implicit SSL/TLS. e.g. port 465" + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :tls, + label: "STARTTLS Mode", + type: {:dropdown, :atom}, + description: "Explicit TLS (STARTTLS) enforcement mode", + suggestions: [:if_available, :always, :never] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :auth, + label: "AUTH Mode", + type: {:dropdown, :atom}, + description: "SMTP AUTH enforcement mode", + suggestions: [:if_available, :always, :never] }, %{ group: {:subgroup, Swoosh.Adapters.SMTP}, key: :retries, type: :integer, - description: "`Swoosh.Adapters.SMTP` adapter specific setting", - suggestions: [5] - }, - %{ - group: {:subgroup, Swoosh.Adapters.SMTP}, - key: :no_mx_lookups, - label: "No MX lookups", - type: :boolean, - description: "`Swoosh.Adapters.SMTP` adapter specific setting" + description: "SMTP temporary (4xx) error retries", + suggestions: [1] }, %{ group: {:subgroup, Swoosh.Adapters.Sendgrid}, key: :api_key, - label: "API key", + label: "SendGrid API Key", type: :string, - description: "`Swoosh.Adapters.Sendgrid` adapter specific setting", - suggestions: ["my-api-key"] + suggestions: ["YOUR_API_KEY"] }, %{ group: {:subgroup, Swoosh.Adapters.Sendmail}, key: :cmd_path, type: :string, - description: "`Swoosh.Adapters.Sendmail` adapter specific setting", suggestions: ["/usr/bin/sendmail"] }, %{ group: {:subgroup, Swoosh.Adapters.Sendmail}, key: :cmd_args, type: :string, - description: "`Swoosh.Adapters.Sendmail` adapter specific setting", suggestions: ["-N delay,failure,success"] }, %{ group: {:subgroup, Swoosh.Adapters.Sendmail}, key: :qmail, - type: :boolean, - description: "`Swoosh.Adapters.Sendmail` adapter specific setting" + label: "Qmail compat mode", + type: :boolean }, %{ group: {:subgroup, Swoosh.Adapters.Mandrill}, key: :api_key, - label: "API key", + label: "Mandrill API Key", type: :string, - description: "`Swoosh.Adapters.Mandrill` adapter specific setting", - suggestions: ["my-api-key"] + suggestions: ["YOUR_API_KEY"] }, %{ group: {:subgroup, Swoosh.Adapters.Mailgun}, key: :api_key, - label: "API key", + label: "Mailgun API Key", type: :string, - description: "`Swoosh.Adapters.Mailgun` adapter specific setting", - suggestions: ["my-api-key"] + suggestions: ["YOUR_API_KEY"] }, %{ group: {:subgroup, Swoosh.Adapters.Mailgun}, key: :domain, type: :string, - description: "`Swoosh.Adapters.Mailgun` adapter specific setting", - suggestions: ["pleroma.com"] + suggestions: ["YOUR_DOMAIN_NAME"] }, %{ group: {:subgroup, Swoosh.Adapters.Mailjet}, key: :api_key, - label: "API key", + label: "MailJet Public API Key", type: :string, - description: "`Swoosh.Adapters.Mailjet` adapter specific setting", - suggestions: ["my-api-key"] + suggestions: ["MJ_APIKEY_PUBLIC"] }, %{ group: {:subgroup, Swoosh.Adapters.Mailjet}, key: :secret, + label: "MailJet Private API Key", type: :string, - description: "`Swoosh.Adapters.Mailjet` adapter specific setting", - suggestions: ["my-secret-key"] + suggestions: ["MJ_APIKEY_PRIVATE"] }, %{ group: {:subgroup, Swoosh.Adapters.Postmark}, key: :api_key, - label: "API key", + label: "Postmark API Key", type: :string, - description: "`Swoosh.Adapters.Postmark` adapter specific setting", - suggestions: ["my-api-key"] + suggestions: ["X-Postmark-Server-Token"] }, %{ group: {:subgroup, Swoosh.Adapters.SparkPost}, key: :api_key, - label: "API key", + label: "SparkPost API key", type: :string, - description: "`Swoosh.Adapters.SparkPost` adapter specific setting", - suggestions: ["my-api-key"] + suggestions: ["YOUR_API_KEY"] }, %{ group: {:subgroup, Swoosh.Adapters.SparkPost}, key: :endpoint, type: :string, - description: "`Swoosh.Adapters.SparkPost` adapter specific setting", suggestions: ["https://api.sparkpost.com/api/v1"] }, - %{ - group: {:subgroup, Swoosh.Adapters.AmazonSES}, - key: :region, - type: :string, - description: "`Swoosh.Adapters.AmazonSES` adapter specific setting", - suggestions: ["us-east-1", "us-east-2"] - }, %{ group: {:subgroup, Swoosh.Adapters.AmazonSES}, key: :access_key, + label: "AWS Access Key", type: :string, - description: "`Swoosh.Adapters.AmazonSES` adapter specific setting", - suggestions: ["aws-access-key"] + suggestions: ["AWS_ACCESS_KEY"] }, %{ group: {:subgroup, Swoosh.Adapters.AmazonSES}, key: :secret, + label: "AWS Secret Key", type: :string, - description: "`Swoosh.Adapters.AmazonSES` adapter specific setting", - suggestions: ["aws-secret-key"] + suggestions: ["AWS_SECRET_KEY"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.AmazonSES}, + key: :region, + label: "AWS Region", + type: :string, + suggestions: ["us-east-1", "us-east-2"] }, %{ group: {:subgroup, Swoosh.Adapters.Dyn}, key: :api_key, - label: "API key", + label: "Dyn API Key", type: :string, - description: "`Swoosh.Adapters.Dyn` adapter specific setting", - suggestions: ["my-api-key"] - }, - %{ - group: {:subgroup, Swoosh.Adapters.SocketLabs}, - key: :server_id, - type: :string, - description: "`Swoosh.Adapters.SocketLabs` adapter specific setting" + suggestions: ["apikey"] }, %{ group: {:subgroup, Swoosh.Adapters.SocketLabs}, key: :api_key, - label: "API key", + label: "SocketLabs API Key", type: :string, - description: "`Swoosh.Adapters.SocketLabs` adapter specific setting" + suggestions: ["INJECTION_API_KEY"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SocketLabs}, + key: :server_id, + label: "Server ID", + type: :string, + suggestions: ["SERVER_ID"] }, %{ group: {:subgroup, Swoosh.Adapters.Gmail}, key: :access_token, + label: "GMail API Access Token", type: :string, - description: "`Swoosh.Adapters.Gmail` adapter specific setting" - } - ] - }, - %{ - group: :swoosh, - type: :group, - description: "`Swoosh.Adapters.Local` adapter specific settings", - children: [ - %{ - group: {:subgroup, Swoosh.Adapters.Local}, - key: :serve_mailbox, - type: :boolean, - description: "Run the preview server together as part of your app" - }, - %{ - group: {:subgroup, Swoosh.Adapters.Local}, - key: :preview_port, - type: :integer, - description: "The preview server port", - suggestions: [4001] + suggestions: ["GMAIL_API_ACCESS_TOKEN"] } ] }, @@ -559,6 +459,42 @@ } ] }, + %{ + group: :pleroma, + key: :features, + type: :group, + description: "Customizable features", + children: [ + %{ + key: :improved_hashtag_timeline, + type: {:dropdown, :atom}, + description: + "Setting to force toggle / force disable improved hashtags timeline. `:enabled` forces hashtags to be fetched from `hashtags` table for hashtags timeline. `:disabled` forces object-embedded hashtags to be used (slower). Keep it `:auto` for automatic behaviour (it is auto-set to `:enabled` [unless overridden] when HashtagsTableMigrator completes).", + suggestions: [:auto, :enabled, :disabled] + } + ] + }, + %{ + group: :pleroma, + key: :populate_hashtags_table, + type: :group, + description: "`populate_hashtags_table` background migration settings", + children: [ + %{ + key: :fault_rate_allowance, + type: :float, + description: + "Max accepted rate of objects that failed in the migration. Any value from 0.0 which tolerates no errors to 1.0 which will enable the feature even if hashtags transfer failed for all records.", + suggestions: [0.01] + }, + %{ + key: :sleep_interval_ms, + type: :integer, + description: + "Sleep interval between each chunk of processed records in order to decrease the load on the system (defaults to 0 and should be keep default on most instances)." + } + ] + }, %{ group: :pleroma, key: :instance, @@ -1254,7 +1190,7 @@ hideSitename: false, hideUserStats: false, loginMethod: "password", - logo: "/static/logo.png", + logo: "/static/logo.svg", logoMargin: ".1em", logoMask: true, minimalScopesMode: false, @@ -1340,7 +1276,7 @@ key: :logo, type: {:string, :image}, description: "URL of the logo, defaults to Pleroma's logo", - suggestions: ["/static/logo.png"] + suggestions: ["/static/logo.svg"] }, %{ key: :logoMargin, @@ -1550,7 +1486,7 @@ %{ key: :enabled, type: :boolean, - description: "Enables proxying of remote media to the instance's proxy" + description: "Enables proxying of remote media via the instance's proxy" }, %{ key: :base_url, @@ -1587,80 +1523,42 @@ }, %{ key: :proxy_opts, - label: "Proxy Options", + label: "Advanced MediaProxy Options", type: :keyword, - description: "Options for Pleroma.ReverseProxy", + description: "Internal Pleroma.ReverseProxy settings", suggestions: [ redirect_on_failure: false, max_body_length: 25 * 1_048_576, - max_read_duration: 30_000, - http: [ - follow_redirect: true, - pool: :media - ] + max_read_duration: 30_000 ], children: [ %{ key: :redirect_on_failure, type: :boolean, - description: - "Redirects the client to the real remote URL if there's any HTTP errors. " <> - "Any error during body processing will not be redirected as the response is chunked." + description: """ + Redirects the client to the origin server upon encountering HTTP errors.\n + Note that files larger than Max Body Length will trigger an error. (e.g., Peertube videos)\n\n + **WARNING:** This setting will allow larger files to be accessed, but exposes the\n + IP addresses of your users to the other servers, bypassing the MediaProxy. + """ }, %{ key: :max_body_length, type: :integer, description: - "Limits the content length to be approximately the " <> - "specified length. It is validated with the `content-length` header and also verified when proxying." + "Maximum file size (in bytes) allowed through the Pleroma MediaProxy cache." }, %{ key: :max_read_duration, type: :integer, - description: "Timeout (in milliseconds) of GET request to remote URI." - }, - %{ - key: :http, - label: "HTTP", - type: :keyword, - description: "HTTP options", - children: [ - %{ - key: :adapter, - type: :keyword, - description: "Adapter specific options", - children: [ - %{ - key: :ssl_options, - type: :keyword, - label: "SSL Options", - description: "SSL options for HTTP adapter", - children: [ - %{ - key: :versions, - type: {:list, :atom}, - description: "List of TLS version to use", - suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"] - } - ] - } - ] - }, - %{ - key: :proxy_url, - label: "Proxy URL", - type: [:string, :tuple], - description: "Proxy URL", - suggestions: ["127.0.0.1:8123", {:socks5, :localhost, 9050}] - } - ] + description: "Timeout (in milliseconds) of GET request to the remote URI." } ] }, %{ key: :whitelist, type: {:list, :string}, - description: "List of hosts with scheme to bypass the mediaproxy", + description: "List of hosts with scheme to bypass the MediaProxy", suggestions: ["http://example.com"] } ] @@ -1698,7 +1596,7 @@ key: :min_content_length, type: :integer, description: - "Min content length to perform preview, in bytes. If greater than 0, media smaller in size will be served as is, without thumbnailing." + "Min content length (in bytes) to perform preview. Media smaller in size will be served without thumbnailing." } ] }, @@ -1736,13 +1634,21 @@ group: :pleroma, key: Pleroma.Web.MediaProxy.Invalidation.Script, type: :group, - description: "Script invalidate settings", + description: "Invalidation script settings", children: [ %{ key: :script_path, type: :string, - description: "Path to shell script. Which will run purge cache.", + description: "Path to executable script which will purge cached items.", suggestions: ["./installation/nginx-cache-purge.sh.example"] + }, + %{ + key: :url_format, + label: "URL Format", + type: :string, + description: + "Optional URL format preprocessing. Only required for Apache's htcacheclean.", + suggestions: [":htcacheclean"] } ] }, @@ -1953,14 +1859,8 @@ group: :pleroma, key: Oban, type: :group, - description: """ - [Oban](https://github.com/sorentwo/oban) asynchronous job processor configuration. - - Note: if you are running PostgreSQL in [`silent_mode`](https://postgresqlco.nf/en/doc/param/silent_mode?version=9.1), - it's advised to set [`log_destination`](https://postgresqlco.nf/en/doc/param/log_destination?version=9.1) to `syslog`, - otherwise `postmaster.log` file may grow because of "you don't own a lock of type ShareLock" warnings - (see https://github.com/sorentwo/oban/issues/52). - """, + description: + "[Oban](https://github.com/sorentwo/oban) asynchronous job processor configuration.", children: [ %{ key: :log, @@ -2990,7 +2890,7 @@ type: :integer, description: "Activity pub routes (except question activities). Default: `nil` (no expiration).", - suggestions: [30_000, nil] + suggestions: [nil] }, %{ key: :activity_pub_question, @@ -3042,6 +2942,23 @@ } ] }, + %{ + group: :pleroma, + key: :mrf_follow_bot, + tab: :mrf, + related_policy: "Pleroma.Web.ActivityPub.MRF.FollowBotPolicy", + label: "MRF FollowBot Policy", + type: :group, + description: "Automatically follows newly discovered accounts.", + children: [ + %{ + key: :follower_nickname, + type: :string, + description: "The name of the bot account to use for following newly discovered users.", + suggestions: ["followbot"] + } + ] + }, %{ group: :pleroma, key: :modules, @@ -3326,6 +3243,12 @@ type: :string, description: "S3 host", suggestions: ["s3.eu-central-1.amazonaws.com"] + }, + %{ + key: :region, + type: :string, + description: "S3 region (for AWS)", + suggestions: ["us-east-1"] } ] }, @@ -3422,9 +3345,9 @@ }, %{ key: :ip_whitelist, + label: "IP Whitelist", type: [{:list, :string}, {:list, :charlist}, {:list, :tuple}], - description: - "[Pleroma extension] If non-empty, restricts access to app metrics endpoint to specified IP addresses." + description: "Restrict access of app metrics endpoint to the specified IP addresses." }, %{ key: :auth, @@ -3445,5 +3368,53 @@ suggestions: [:text, :protobuf] } ] + }, + %{ + group: :pleroma, + key: ConcurrentLimiter, + type: :group, + description: "Limits configuration for background tasks.", + children: [ + %{ + key: Pleroma.Web.RichMedia.Helpers, + type: :keyword, + description: "Concurrent limits configuration for getting RichMedia for activities.", + suggestions: [max_running: 5, max_waiting: 5], + children: [ + %{ + key: :max_running, + type: :integer, + description: "Max running concurrently jobs.", + suggestion: [5] + }, + %{ + key: :max_waiting, + type: :integer, + description: "Max waiting jobs.", + suggestion: [5] + } + ] + }, + %{ + key: Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, + type: :keyword, + description: "Concurrent limits configuration for MediaProxyWarmingPolicy.", + suggestions: [max_running: 5, max_waiting: 5], + children: [ + %{ + key: :max_running, + type: :integer, + description: "Max running concurrently jobs.", + suggestion: [5] + }, + %{ + key: :max_waiting, + type: :integer, + description: "Max waiting jobs.", + suggestion: [5] + } + ] + } + ] } ] diff --git a/config/emoji.txt b/config/emoji.txt index 200768ad1..52b714ee5 100644 --- a/config/emoji.txt +++ b/config/emoji.txt @@ -1,2 +1,3 @@ firefox, /emoji/Firefox.gif, Gif,Fun blank, /emoji/blank.png, Fun +dinosaur, /emoji/dino walking.gif, Gif diff --git a/config/test.exs b/config/test.exs index 2a20a03e7..87396a88d 100644 --- a/config/test.exs +++ b/config/test.exs @@ -38,7 +38,7 @@ external_user_synchronization: false, static_dir: "test/instance_static/" -config :pleroma, :activitypub, sign_object_fetches: false +config :pleroma, :activitypub, sign_object_fetches: false, follow_handshake_timeout: 0 # Configure your database config :pleroma, Pleroma.Repo, @@ -47,10 +47,13 @@ password: "postgres", database: "pleroma_test", hostname: System.get_env("DB_HOST") || "localhost", - pool: Ecto.Adapters.SQL.Sandbox + pool: Ecto.Adapters.SQL.Sandbox, + pool_size: 50 + +config :pleroma, :dangerzone, override_repo_pool_size: true # Reduce hash rounds for testing -config :pbkdf2_elixir, rounds: 1 +config :pleroma, :password, iterations: 1 config :tesla, adapter: Tesla.Mock @@ -112,15 +115,24 @@ config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: true -config :pleroma, Pleroma.Uploaders.S3, - bucket: nil, - streaming_enabled: true, - public_endpoint: nil - config :tzdata, :autoupdate, :disabled config :pleroma, :mrf, policies: [] +config :pleroma, :pipeline, + object_validator: Pleroma.Web.ActivityPub.ObjectValidatorMock, + mrf: Pleroma.Web.ActivityPub.MRFMock, + activity_pub: Pleroma.Web.ActivityPub.ActivityPubMock, + side_effects: Pleroma.Web.ActivityPub.SideEffectsMock, + federator: Pleroma.Web.FederatorMock, + config: Pleroma.ConfigMock + +config :pleroma, :cachex, provider: Pleroma.CachexMock + +config :pleroma, :side_effects, + ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock, + logger: Pleroma.LoggerMock + if File.exists?("./config/test.secret.exs") do import_config "test.secret.exs" else diff --git a/docs/administration/CLI_tasks/config.md b/docs/administration/CLI_tasks/config.md index 000ed4d98..fc9f3cbd5 100644 --- a/docs/administration/CLI_tasks/config.md +++ b/docs/administration/CLI_tasks/config.md @@ -32,16 +32,20 @@ config :pleroma, configurable_from_database: false ``` -To delete transferred settings from database optional flag `-d` can be used. `` is `prod` by default. +Options: + +- `` - where to save migrated config. E.g. `--path=/tmp`. If file saved into non standart folder, you must manually copy file into directory where Pleroma can read it. For OTP install path will be `PLEROMA_CONFIG_PATH` or `/etc/pleroma`. For installation from source - `config` directory in the pleroma folder. +- `` - environment, for which is migrated config. By default is `prod`. +- To delete transferred settings from database optional flag `-d` can be used === "OTP" ```sh - ./bin/pleroma_ctl config migrate_from_db [--env=] [-d] + ./bin/pleroma_ctl config migrate_from_db [--env=] [-d] [--path=] ``` === "From Source" ```sh - mix pleroma.config migrate_from_db [--env=] [-d] + mix pleroma.config migrate_from_db [--env=] [-d] [--path=] ``` ## Dump all of the config settings defined in the database diff --git a/docs/administration/CLI_tasks/database.md b/docs/administration/CLI_tasks/database.md index 6dca83167..c53c49921 100644 --- a/docs/administration/CLI_tasks/database.md +++ b/docs/administration/CLI_tasks/database.md @@ -141,3 +141,21 @@ but should only be run if necessary. **It is safe to cancel this.** ```sh mix pleroma.database ensure_expiration ``` + +## Change Text Search Configuration + +Change `default_text_search_config` for database and (if necessary) text_search_config used in index, then rebuild index (it may take time). + +=== "OTP" + + ```sh + ./bin/pleroma_ctl database set_text_search_config english + ``` + +=== "From Source" + + ```sh + mix pleroma.database set_text_search_config english + ``` + +See [PostgreSQL documentation](https://www.postgresql.org/docs/current/textsearch-configuration.html) and `docs/configuration/howto_search_cjk.md` for more detail. diff --git a/docs/administration/CLI_tasks/email.md b/docs/administration/CLI_tasks/email.md index d9aa0e71b..2bb57bea4 100644 --- a/docs/administration/CLI_tasks/email.md +++ b/docs/administration/CLI_tasks/email.md @@ -16,8 +16,7 @@ mix pleroma.email test [--to ] ``` - -Example: +Example: === "OTP" @@ -36,11 +35,11 @@ Example: === "OTP" ```sh - ./bin/pleroma_ctl email send_confirmation_mails + ./bin/pleroma_ctl email resend_confirmation_emails ``` === "From Source" ```sh - mix pleroma.email send_confirmation_mails + mix pleroma.email resend_confirmation_emails ``` diff --git a/docs/administration/CLI_tasks/user.md b/docs/administration/CLI_tasks/user.md index c64ed4f22..24fdaeab4 100644 --- a/docs/administration/CLI_tasks/user.md +++ b/docs/administration/CLI_tasks/user.md @@ -133,22 +133,20 @@ mix pleroma.user sign_out ``` - -## Deactivate or activate a user +## Activate a user === "OTP" ```sh - ./bin/pleroma_ctl user toggle_activated + ./bin/pleroma_ctl user activate NICKNAME ``` === "From Source" ```sh - mix pleroma.user toggle_activated + mix pleroma.user activate NICKNAME ``` - ## Deactivate a user and unsubscribes local users from the user === "OTP" @@ -264,13 +262,13 @@ === "OTP" ```sh - ./bin/pleroma_ctl user toggle_confirmed + ./bin/pleroma_ctl user confirm ``` === "From Source" ```sh - mix pleroma.user toggle_confirmed + mix pleroma.user confirm ``` ## Set confirmation status for all regular active users diff --git a/docs/clients.md b/docs/clients.md index 3d81763e1..5650ea236 100644 --- a/docs/clients.md +++ b/docs/clients.md @@ -17,7 +17,7 @@ Feel free to contact us to be added to this list! - Features: MastoAPI ### Whalebird -- Homepage: +- Homepage: - Source Code: - Contact: [@h3poteto@pleroma.io](https://pleroma.io/users/h3poteto) - Platforms: Windows, Mac, Linux diff --git a/docs/configuration/auth.md b/docs/configuration/auth.md new file mode 100644 index 000000000..c80f094e7 --- /dev/null +++ b/docs/configuration/auth.md @@ -0,0 +1 @@ +See `Authentication` section of [the configuration cheatsheet](../configuration/cheatsheet.md#authentication). diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 85551362c..069421722 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -49,7 +49,7 @@ To add configuration to your config file, you can copy it from the base config. * `attachment_links`: Set to true to enable automatically adding attachment link text to statuses. * `max_report_comment_size`: The maximum size of the report comment (Default: `1000`). * `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). Default: `false`. -* `healthcheck`: If set to true, system data will be shown on ``/api/pleroma/healthcheck``. +* `healthcheck`: If set to true, system data will be shown on ``/api/v1/pleroma/healthcheck``. * `remote_post_retention_days`: The default amount of days to retain remote posts when pruning the database. * `user_bio_length`: A user bio maximum length (default: `5000`). * `user_name_length`: A user name maximum length (default: `100`). @@ -65,6 +65,13 @@ To add configuration to your config file, you can copy it from the base config. * `show_reactions`: Let favourites and emoji reactions be viewed through the API (default: `true`). * `password_reset_token_validity`: The time after which reset tokens aren't accepted anymore, in seconds (default: one day). +## :database +* `improved_hashtag_timeline`: Setting to force toggle / force disable improved hashtags timeline. `:enabled` forces hashtags to be fetched from `hashtags` table for hashtags timeline. `:disabled` forces object-embedded hashtags to be used (slower). Keep it `:auto` for automatic behaviour (it is auto-set to `:enabled` [unless overridden] when HashtagsTableMigrator completes). + +## Background migrations +* `populate_hashtags_table/sleep_interval_ms`: Sleep interval between each chunk of processed records in order to decrease the load on the system (defaults to 0 and should be keep default on most instances). +* `populate_hashtags_table/fault_rate_allowance`: Max rate of failed objects to actually processed objects in order to enable the feature (any value from 0.0 which tolerates no errors to 1.0 which will enable the feature even if hashtags transfer failed for all records). + ## Welcome * `direct_message`: - welcome message sent as a direct message. * `enabled`: Enables the send a direct message to a newly registered user. Defaults to `false`. @@ -117,6 +124,7 @@ To add configuration to your config file, you can copy it from the base config. * `Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy`: Rejects or delists posts based on their age when received. (See [`:mrf_object_age`](#mrf_object_age)). * `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.Workers.PurgeExpiredActivity` to be enabled for processing the scheduled delections. * `Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy`: Makes all bot posts to disappear from public timelines. + * `Pleroma.Web.ActivityPub.MRF.FollowBotPolicy`: Automatically follows newly discovered users from the specified bot account. Local accounts, locked accounts, and users with "#nobot" in their bio are respected and excluded from being followed. * `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo). * `transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value. @@ -203,6 +211,21 @@ config :pleroma, :mrf_user_allowlist, %{ * `days`: Default global expiration time for all local Create activities (in days) +#### :mrf_hashtag + +* `sensitive`: List of hashtags to mark activities as sensitive (default: `nsfw`) +* `federated_timeline_removal`: List of hashtags to remove activities from the federated timeline (aka TWNK) +* `reject`: List of hashtags to reject activities from + +Notes: +- The hashtags in the configuration do not have a leading `#`. +- This MRF Policy is always enabled, if you want to disable it you have to set empty lists + +#### :mrf_follow_bot + +* `follower_nickname`: The name of the bot account to use for following newly discovered users. Using `followbot` or similar is strongly suggested. + + ### :activitypub * `unfollow_blocked`: Whether blocks result in people getting unfollowed * `outgoing_blocks`: Whether to federate blocks to other instances @@ -225,7 +248,7 @@ config :pleroma, :mrf_user_allowlist, %{ This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` and `masto_fe` are configured. You can find the documentation for `pleroma_fe` configuration into [Pleroma-FE configuration and customization for instance administrators](/frontend/CONFIGURATION/#options). -Frontends can access these settings at `/api/pleroma/frontend_configurations` +Frontends can access these settings at `/api/v1/pleroma/frontend_configurations` To add your own configuration for PleromaFE, use it like this: @@ -321,9 +344,10 @@ This section describe PWA manifest instance-specific values. Currently this opti #### Pleroma.Web.MediaProxy.Invalidation.Script This strategy allow perform external shell script to purge cache. -Urls of attachments pass to script as arguments. +Urls of attachments are passed to the script as arguments. -* `script_path`: path to external script. +* `script_path`: Path to the external script. +* `url_format`: Set to `:htcacheclean` if using Apache's htcacheclean utility. Example: @@ -549,7 +573,7 @@ the source code is here: [kocaptcha](https://github.com/koto-bank/kocaptcha). Th * `uploader`: Which one of the [uploaders](#uploaders) to use. * `filters`: List of [upload filters](#upload-filters) to use. * `link_name`: When enabled Pleroma will add a `name` parameter to the url of the upload, for example `https://instance.tld/media/corndog.png?name=corndog.png`. This is needed to provide the correct filename in Content-Disposition headers when using filters like `Pleroma.Upload.Filter.Dedupe` -* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host. +* `base_url`: The base URL to access a user-uploaded file. Useful when you want to host the media files via another domain or are using a 3rd party S3 provider. * `proxy_remote`: If you're using a remote uploader, Pleroma will proxy media requests instead of redirecting to it. * `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation. * `filename_display_max_length`: Set max length of a filename to display. 0 = no limit. Default: 30. @@ -570,10 +594,7 @@ Don't forget to configure [Ex AWS S3](#ex-aws-s3-settings) * `bucket`: S3 bucket name. * `bucket_namespace`: S3 bucket namespace. -* `public_endpoint`: S3 endpoint that the user finally accesses(ex. "https://s3.dualstack.ap-northeast-1.amazonaws.com") * `truncated_namespace`: If you use S3 compatible service such as Digital Ocean Spaces or CDN, set folder name or "" etc. -For example, when using CDN to S3 virtual host format, set "". -At this time, write CNAME to CDN in public_endpoint. * `streaming_enabled`: Enable streaming uploads, when enabled the file will be sent to the server in chunks as it's being read. This may be unsupported by some providers, try disabling this if you have upload problems. #### Ex AWS S3 settings @@ -850,13 +871,13 @@ config :pleroma, :admin_token, "somerandomtoken" You can then do ```shell -curl "http://localhost:4000/api/pleroma/admin/users/invites?admin_token=somerandomtoken" +curl "http://localhost:4000/api/v1/pleroma/admin/users/invites?admin_token=somerandomtoken" ``` or ```shell -curl -H "X-Admin-Token: somerandomtoken" "http://localhost:4000/api/pleroma/admin/users/invites" +curl -H "X-Admin-Token: somerandomtoken" "http://localhost:4000/api/v1/pleroma/admin/users/invites" ``` Warning: it's discouraged to use this feature because of the associated security risk: static / rarely changed instance-wide token is much weaker compared to email-password pair of a real admin user; consider using HTTP Basic Auth or OAuth-based authentication instead. @@ -895,6 +916,22 @@ Pleroma account will be created with the same name as the LDAP user name. Note, if your LDAP server is an Active Directory server the correct value is commonly `uid: "cn"`, but if you use an OpenLDAP server the value may be `uid: "uid"`. +### :oauth2 (Pleroma as OAuth 2.0 provider settings) + +OAuth 2.0 provider settings: + +* `token_expires_in` - The lifetime in seconds of the access token. +* `issue_new_refresh_token` - Keeps old refresh token or generate new refresh token when to obtain an access token. +* `clean_expired_tokens` - Enable a background job to clean expired oauth tokens. Defaults to `false`. + +OAuth 2.0 provider and related endpoints: + +* `POST /api/v1/apps` creates client app basing on provided params. +* `GET/POST /oauth/authorize` renders/submits authorization form. +* `POST /oauth/token` creates/renews OAuth token. +* `POST /oauth/revoke` revokes provided OAuth token. +* `GET /api/v1/accounts/verify_credentials` (with proper `Authorization` header or `access_token` URI param) returns user info on requester (with `acct` field containing local nickname and `fqn` field containing fully-qualified nickname which could generally be used as email stub for OAuth software that demands email field in identity endpoint response, like Peertube). + ### OAuth consumer mode OAuth consumer mode allows sign in / sign up via external OAuth providers (e.g. Twitter, Facebook, Google, Microsoft, etc.). @@ -967,14 +1004,6 @@ config :ueberauth, Ueberauth, ] ``` -### OAuth 2.0 provider - :oauth2 - -Configure OAuth 2 provider capabilities: - -* `token_expires_in` - The lifetime in seconds of the access token. -* `issue_new_refresh_token` - Keeps old refresh token or generate new refresh token when to obtain an access token. -* `clean_expired_tokens` - Enable a background job to clean expired oauth tokens. Defaults to `false`. - ## Link parsing ### :uri_schemes @@ -1113,3 +1142,15 @@ Settings to enable and configure expiration for ephemeral activities * `:enabled` - enables ephemeral activities creation * `:min_lifetime` - minimum lifetime for ephemeral activities (in seconds). Default: 10 minutes. + +## ConcurrentLimiter + +Settings to restrict concurrently running jobs. Jobs which can be configured: + +* `Pleroma.Web.RichMedia.Helpers` - generating link previews of URLs in activities +* `Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy` - warming remote media cache via MediaProxyWarmingPolicy + +Each job has these settings: + +* `:max_running` - max concurrently runnings jobs +* `:max_waiting` - max waiting jobs diff --git a/docs/configuration/howto_search_cjk.md b/docs/configuration/howto_search_cjk.md new file mode 100644 index 000000000..d3ce28077 --- /dev/null +++ b/docs/configuration/howto_search_cjk.md @@ -0,0 +1,42 @@ +# How to enable text search for Chinese, Japanese and Korean + +Pleroma's full text search feature is powered by PostgreSQL's native [text search](https://www.postgresql.org/docs/current/textsearch.html), it works well out of box for most of languages, but needs extra configurations for some asian languages like Chinese, Japanese and Korean (CJK). + + +## Setup and test the new search config + +In most cases, you would need an extension installed to support parsing CJK text. Here are a few extension you may choose from, or you are more than welcome to share additional ones you found working for you with the rest of Pleroma community. + + * [a generic n-gram parser](https://github.com/huangjimmy/pg_cjk_parser) supports Simplifed/Traditional Chinese, Japanese, and Korean + * [a Korean parser](https://github.com/i0seph/textsearch_ko) based on mecab + * [a Japanese parser](https://www.amris.co.jp/tsja/index.html) based on mecab + * [zhparser](https://github.com/amutu/zhparser/) is a PostgreSQL extension base on the Simple Chinese Word Segmentation(SCWS) + * [another Chinese parser](https://github.com/jaiminpan/pg_jieba) based on Jieba Chinese Word Segmentation + +Once you have the new search config , make sure you test it with the `pleroma` user in PostgreSQL (change `YOUR.CONFIG` to your real configuration name) +``` +SELECT ts_debug('YOUR.CONFIG', '安装和配置Nginx, ElixirとErlangをインストールします'); +``` +Check output of the query, and see if it matches your expectation. + + +## Update text search config and index in database + +=== "OTP" + + ```sh + ./bin/pleroma_ctl database set_text_search_config YOUR.CONFIG + ``` + +=== "From Source" + + ```sh + mix pleroma.database set_text_search_config YOUR.CONFIG + ``` + +Note: index update may take a while. + +## Restart database connection +Since some changes above will only apply with a new database connection, you will have to restart either Pleroma or PostgreSQL process, or use `pg_terminate_backend` SQL command without restarting either. + +Now the search results of statuses should be much more friendly for your language of choice, the results for searching users and tags were not changed, as the default parsing/matching should work for most cases. diff --git a/docs/configuration/mrf.md b/docs/configuration/mrf.md index 31c66e098..9e8c0a2d7 100644 --- a/docs/configuration/mrf.md +++ b/docs/configuration/mrf.md @@ -133,3 +133,26 @@ config :pleroma, :mrf, ``` Please note that the Pleroma developers consider custom MRF policy modules to fall under the purview of the AGPL. As such, you are obligated to release the sources to your custom MRF policy modules upon request. + +### MRF policies descriptions + +If MRF policy depends on config, it can be added into MRF tab to adminFE by adding `config_description/0` method, which returns a map with a specific structure. See existing MRF's like `lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex` for examples. Note that more complex inputs, like tuples or maps, may need extra changes in the adminFE and just adding it to `config_description/0` may not be enough to get these inputs working from the adminFE. + +Example: + +```elixir +%{ + key: :mrf_activity_expiration, + related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy", + label: "MRF Activity Expiration Policy", + description: "Adds automatic expiration to all local activities", + children: [ + %{ + key: :days, + type: :integer, + description: "Default global expiration time for all local activities (in days)", + suggestions: [90, 365] + } + ] + } +``` diff --git a/docs/configuration/postgresql.md b/docs/configuration/postgresql.md index 6983fb459..e251eb83b 100644 --- a/docs/configuration/postgresql.md +++ b/docs/configuration/postgresql.md @@ -1,10 +1,28 @@ -# Optimizing your PostgreSQL performance +# Optimizing PostgreSQL performance -Pleroma performance depends to a large extent on good database performance. The default PostgreSQL settings are mostly fine, but often you can get better performance by changing a few settings. +Pleroma performance is largely dependent on performance of the underlying database. Better performance can be achieved by adjusting a few settings. -You can use [PGTune](https://pgtune.leopard.in.ua) to get recommendations for your setup. If you do, set the "Number of Connections" field to 20, as Pleroma will only use 10 concurrent connections anyway. If you don't, it will give you advice that might even hurt your performance. +## PGTune -We also recommend not using the "Network Storage" option. +[PgTune](https://pgtune.leopard.in.ua) can be used to get recommended settings. Be sure to set "Number of Connections" to 20, otherwise it might produce settings hurtful to database performance. It is also recommended to not use "Network Storage" option. + +## Disable generic query plans + +When PostgreSQL receives a query, it decides on a strategy for searching the requested data, this is called a query plan. The query planner has two modes: generic and custom. Generic makes a plan for all queries of the same shape, ignoring the parameters, which is then cached and reused. Custom, on the contrary, generates a unique query plan based on query parameters. + +By default PostgreSQL has an algorithm to decide which mode is more efficient for particular query, however this algorithm has been observed to be wrong on some of the queries Pleroma sends, leading to serious performance loss. Therefore, it is recommended to disable generic mode. + + +Pleroma already avoids generic query plans by default, however the method it uses is not the most efficient because it needs to be compatible with all supported PostgreSQL versions. For PostgreSQL 12 and higher additional performance can be gained by adding the following to Pleroma configuration: +```elixir +config :pleroma, Pleroma.Repo, + prepare: :named, + parameters: [ + plan_cache_mode: "force_custom_plan" + ] +``` + +A more detailed explaination of the issue can be found at . ## Example configurations @@ -28,4 +46,3 @@ max_worker_processes = 2 max_parallel_workers_per_gather = 1 max_parallel_workers = 2 ``` - diff --git a/docs/API/admin_api.md b/docs/development/API/admin_api.md similarity index 86% rename from docs/API/admin_api.md rename to docs/development/API/admin_api.md index 266f8cef8..8f855d251 100644 --- a/docs/API/admin_api.md +++ b/docs/development/API/admin_api.md @@ -2,14 +2,9 @@ Authentication is required and the user must be an admin. -Configuration options: +The `/api/v1/pleroma/admin/*` path is backwards compatible with `/api/pleroma/admin/*` (`/api/pleroma/admin/*` will be deprecated in the future). -* `[:auth, :enforce_oauth_admin_scope_usage]` — OAuth admin scope requirement toggle. - If `true`, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token (client app must support admin scopes). - If `false` and token doesn't have admin scope(s), `is_admin` user flag grants access to admin-specific actions. - Note that client app needs to explicitly support admin scopes and request them when obtaining auth token. - -## `GET /api/pleroma/admin/users` +## `GET /api/v1/pleroma/admin/users` ### List users @@ -30,7 +25,7 @@ Configuration options: - *optional* `actor_types`: **[string]** actor type list (`Person`, `Service`, `Application`) - *optional* `name`: **string** user display name - *optional* `email`: **string** user email -- Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10&tags[]=some_tag&tags[]=another_tag&name=display_name&email=email@example.com` +- Example: `https://mypleroma.org/api/v1/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10&tags[]=some_tag&tags[]=another_tag&name=display_name&email=email@example.com` - Response: ```json @@ -59,7 +54,7 @@ Configuration options: } ``` -## DEPRECATED `DELETE /api/pleroma/admin/users` +## DEPRECATED `DELETE /api/v1/pleroma/admin/users` ### Remove a user @@ -67,7 +62,7 @@ Configuration options: - `nickname` - Response: User’s nickname -## `DELETE /api/pleroma/admin/users` +## `DELETE /api/v1/pleroma/admin/users` ### Remove a user @@ -88,7 +83,7 @@ Configuration options: ] - Response: User’s nickname -## `POST /api/pleroma/admin/users/follow` +## `POST /api/v1/pleroma/admin/users/follow` ### Make a user follow another user @@ -98,7 +93,7 @@ Configuration options: - Response: - "ok" -## `POST /api/pleroma/admin/users/unfollow` +## `POST /api/v1/pleroma/admin/users/unfollow` ### Make a user unfollow another user @@ -108,7 +103,7 @@ Configuration options: - Response: - "ok" -## `PATCH /api/pleroma/admin/users/:nickname/toggle_activation` +## `PATCH /api/v1/pleroma/admin/users/:nickname/toggle_activation` ### Toggle user activation @@ -124,7 +119,7 @@ Configuration options: } ``` -## `PUT /api/pleroma/admin/users/tag` +## `PUT /api/v1/pleroma/admin/users/tag` ### Tag a list of users @@ -132,7 +127,7 @@ Configuration options: - `nicknames` (array) - `tags` (array) -## `DELETE /api/pleroma/admin/users/tag` +## `DELETE /api/v1/pleroma/admin/users/tag` ### Untag a list of users @@ -140,7 +135,7 @@ Configuration options: - `nicknames` (array) - `tags` (array) -## `GET /api/pleroma/admin/users/:nickname/permission_group` +## `GET /api/v1/pleroma/admin/users/:nickname/permission_group` ### Get user user permission groups membership @@ -154,7 +149,7 @@ Configuration options: } ``` -## `GET /api/pleroma/admin/users/:nickname/permission_group/:permission_group` +## `GET /api/v1/pleroma/admin/users/:nickname/permission_group/:permission_group` Note: Available `:permission_group` is currently moderator and admin. 404 is returned when the permission group doesn’t exist. @@ -170,7 +165,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret } ``` -## DEPRECATED `POST /api/pleroma/admin/users/:nickname/permission_group/:permission_group` +## DEPRECATED `POST /api/v1/pleroma/admin/users/:nickname/permission_group/:permission_group` ### Add user to permission group @@ -179,7 +174,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret - On failure: `{"error": "…"}` - On success: JSON of the user -## `POST /api/pleroma/admin/users/permission_group/:permission_group` +## `POST /api/v1/pleroma/admin/users/permission_group/:permission_group` ### Add users to permission group @@ -189,9 +184,9 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret - On failure: `{"error": "…"}` - On success: JSON of the user -## DEPRECATED `DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` +## DEPRECATED `DELETE /api/v1/pleroma/admin/users/:nickname/permission_group/:permission_group` -## `DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` +## `DELETE /api/v1/pleroma/admin/users/:nickname/permission_group/:permission_group` ### Remove user from permission group @@ -201,7 +196,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret - On success: JSON of the user - Note: An admin cannot revoke their own admin status. -## `DELETE /api/pleroma/admin/users/permission_group/:permission_group` +## `DELETE /api/v1/pleroma/admin/users/permission_group/:permission_group` ### Remove users from permission group @@ -212,7 +207,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret - On success: JSON of the user - Note: An admin cannot revoke their own admin status. -## `PATCH /api/pleroma/admin/users/activate` +## `PATCH /api/v1/pleroma/admin/users/activate` ### Activate user @@ -230,7 +225,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret } ``` -## `PATCH /api/pleroma/admin/users/deactivate` +## `PATCH /api/v1/pleroma/admin/users/deactivate` ### Deactivate user @@ -248,7 +243,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret } ``` -## `PATCH /api/pleroma/admin/users/approve` +## `PATCH /api/v1/pleroma/admin/users/approve` ### Approve user @@ -266,7 +261,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret } ``` -## `GET /api/pleroma/admin/users/:nickname_or_id` +## `GET /api/v1/pleroma/admin/users/:nickname_or_id` ### Retrive the details of a user @@ -276,7 +271,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret - On failure: `Not found` - On success: JSON of the user -## `GET /api/pleroma/admin/users/:nickname_or_id/statuses` +## `GET /api/v1/pleroma/admin/users/:nickname_or_id/statuses` ### Retrive user's latest statuses @@ -287,9 +282,20 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret - *optional* `with_reblogs`: `true`/`false` – allows to see reblogs (default is false) - Response: - On failure: `Not found` - - On success: JSON array of user's latest statuses + - On success: JSON, where: + - `total`: total count of the statuses for the user + - `activities`: list of the statuses for the user -## `GET /api/pleroma/admin/instances/:instance/statuses` +```json +{ + "total" : 1, + "activities": [ + // activities list + ] +} +``` + +## `GET /api/v1/pleroma/admin/instances/:instance/statuses` ### Retrive instance's latest statuses @@ -300,9 +306,20 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret - *optional* `with_reblogs`: `true`/`false` – allows to see reblogs (default is false) - Response: - On failure: `Not found` - - On success: JSON array of instance's latest statuses + - On success: JSON, where: + - `total`: total count of the statuses for the instance + - `activities`: list of the statuses for the instance -## `GET /api/pleroma/admin/statuses` +```json +{ + "total" : 1, + "activities": [ + // activities list + ] +} +``` + +## `GET /api/v1/pleroma/admin/statuses` ### Retrives all latest statuses @@ -315,7 +332,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret - On failure: `Not found` - On success: JSON array of user's latest statuses -## `GET /api/pleroma/admin/relay` +## `GET /api/v1/pleroma/admin/relay` ### List Relays @@ -331,7 +348,7 @@ Response: ] ``` -## `POST /api/pleroma/admin/relay` +## `POST /api/v1/pleroma/admin/relay` ### Follow a Relay @@ -347,7 +364,7 @@ Response: {"actor": "https://example.com/relay", "followed_back": true} ``` -## `DELETE /api/pleroma/admin/relay` +## `DELETE /api/v1/pleroma/admin/relay` ### Unfollow a Relay @@ -363,7 +380,7 @@ Response: {"https://example.com/relay"} ``` -## `POST /api/pleroma/admin/users/invite_token` +## `POST /api/v1/pleroma/admin/users/invite_token` ### Create an account registration invite token @@ -384,7 +401,7 @@ Response: } ``` -## `GET /api/pleroma/admin/users/invites` +## `GET /api/v1/pleroma/admin/users/invites` ### Get a list of generated invites @@ -409,7 +426,7 @@ Response: } ``` -## `POST /api/pleroma/admin/users/revoke_invite` +## `POST /api/v1/pleroma/admin/users/revoke_invite` ### Revoke invite by token @@ -430,7 +447,7 @@ Response: } ``` -## `POST /api/pleroma/admin/users/email_invite` +## `POST /api/v1/pleroma/admin/users/email_invite` ### Sends registration invite via email @@ -451,7 +468,7 @@ Response: ] ``` -## `GET /api/pleroma/admin/users/:nickname/password_reset` +## `GET /api/v1/pleroma/admin/users/:nickname/password_reset` ### Get a password reset token for a given nickname @@ -462,11 +479,11 @@ Response: ```json { "token": "base64 reset token", - "link": "https://pleroma.social/api/pleroma/password_reset/url-encoded-base64-token" + "link": "https://pleroma.social/api/v1/pleroma/password_reset/url-encoded-base64-token" } ``` -## `PATCH /api/pleroma/admin/users/force_password_reset` +## `PATCH /api/v1/pleroma/admin/users/force_password_reset` ### Force passord reset for a user with a given nickname @@ -474,7 +491,7 @@ Response: - `nicknames` - Response: none (code `204`) -## PUT `/api/pleroma/admin/users/disable_mfa` +## PUT `/api/v1/pleroma/admin/users/disable_mfa` ### Disable mfa for user's account. @@ -482,7 +499,7 @@ Response: - `nickname` - Response: User’s nickname -## `GET /api/pleroma/admin/users/:nickname/credentials` +## `GET /api/v1/pleroma/admin/users/:nickname/credentials` ### Get the user's email, password, display and settings-related fields @@ -530,7 +547,7 @@ Response: } ``` -## `PATCH /api/pleroma/admin/users/:nickname/credentials` +## `PATCH /api/v1/pleroma/admin/users/:nickname/credentials` ### Change the user's email, password, display and settings-related fields @@ -581,7 +598,7 @@ Status: 404 {"error": "Not found"} ``` -## `GET /api/pleroma/admin/reports` +## `GET /api/v1/pleroma/admin/reports` ### Get a list of reports @@ -741,17 +758,17 @@ Status: 404 } ``` -## `GET /api/pleroma/admin/grouped_reports` +## `GET /api/v1/pleroma/admin/grouped_reports` ### Get a list of reports, grouped by status - Params: none - On success: JSON, returns a list of reports, where: - `date`: date of the latest report - - `account`: the user who has been reported (see `/api/pleroma/admin/reports` for reference) - - `status`: reported status (see `/api/pleroma/admin/reports` for reference) - - `actors`: users who had reported this status (see `/api/pleroma/admin/reports` for reference) - - `reports`: reports (see `/api/pleroma/admin/reports` for reference) + - `account`: the user who has been reported (see `/api/v1/pleroma/admin/reports` for reference) + - `status`: reported status (see `/api/v1/pleroma/admin/reports` for reference) + - `actors`: users who had reported this status (see `/api/v1/pleroma/admin/reports` for reference) + - `reports`: reports (see `/api/v1/pleroma/admin/reports` for reference) ```json "reports": [ @@ -765,7 +782,7 @@ Status: 404 ] ``` -## `GET /api/pleroma/admin/reports/:id` +## `GET /api/v1/pleroma/admin/reports/:id` ### Get an individual report @@ -777,7 +794,7 @@ Status: 404 - 404 Not Found `"Not found"` - On success: JSON, Report object (see above) -## `PATCH /api/pleroma/admin/reports` +## `PATCH /api/v1/pleroma/admin/reports` ### Change the state of one or multiple reports @@ -808,7 +825,7 @@ Status: 404 - On success: `204`, empty response -## `POST /api/pleroma/admin/reports/:id/notes` +## `POST /api/v1/pleroma/admin/reports/:id/notes` ### Create report note @@ -820,7 +837,7 @@ Status: 404 - 400 Bad Request `"Invalid parameters"` when `status` is missing - On success: `204`, empty response -## `DELETE /api/pleroma/admin/reports/:report_id/notes/:id` +## `DELETE /api/v1/pleroma/admin/reports/:report_id/notes/:id` ### Delete report note @@ -832,7 +849,7 @@ Status: 404 - 400 Bad Request `"Invalid parameters"` when `status` is missing - On success: `204`, empty response -## `GET /api/pleroma/admin/statuses/:id` +## `GET /api/v1/pleroma/admin/statuses/:id` ### Show status by id @@ -843,7 +860,7 @@ Status: 404 - 404 Not Found `"Not Found"` - On success: JSON, Mastodon Status entity -## `PUT /api/pleroma/admin/statuses/:id` +## `PUT /api/v1/pleroma/admin/statuses/:id` ### Change the scope of an individual reported status @@ -858,7 +875,7 @@ Status: 404 - 404 Not Found `"Not found"` - On success: JSON, Mastodon Status entity -## `DELETE /api/pleroma/admin/statuses/:id` +## `DELETE /api/v1/pleroma/admin/statuses/:id` ### Delete an individual reported status @@ -870,7 +887,7 @@ Status: 404 - 404 Not Found `"Not found"` - On success: 200 OK `{}` -## `GET /api/pleroma/admin/restart` +## `GET /api/v1/pleroma/admin/restart` ### Restarts pleroma application @@ -885,7 +902,7 @@ Status: 404 {} ``` -## `GET /api/pleroma/admin/need_reboot` +## `GET /api/v1/pleroma/admin/need_reboot` ### Returns the flag whether the pleroma should be restarted @@ -898,7 +915,7 @@ Status: 404 } ``` -## `GET /api/pleroma/admin/config` +## `GET /api/v1/pleroma/admin/config` ### Get list of merged default settings with saved in database. @@ -925,7 +942,7 @@ Status: 404 } ``` -## `POST /api/pleroma/admin/config` +## `POST /api/v1/pleroma/admin/config` ### Update config settings @@ -1074,7 +1091,7 @@ config :quack, } ``` -## ` GET /api/pleroma/admin/config/descriptions` +## ` GET /api/v1/pleroma/admin/config/descriptions` ### Get JSON with config descriptions. Loads json generated from `config/descriptions.exs`. @@ -1107,7 +1124,7 @@ Loads json generated from `config/descriptions.exs`. }] ``` -## `GET /api/pleroma/admin/moderation_log` +## `GET /api/v1/pleroma/admin/moderation_log` ### Get moderation log @@ -1123,6 +1140,7 @@ Loads json generated from `config/descriptions.exs`. ```json [ { + "id": 1234, "data": { "actor": { "id": 1, @@ -1136,7 +1154,7 @@ Loads json generated from `config/descriptions.exs`. ] ``` -## `POST /api/pleroma/admin/reload_emoji` +## `POST /api/v1/pleroma/admin/reload_emoji` ### Reload the instance's custom emoji @@ -1144,7 +1162,7 @@ Loads json generated from `config/descriptions.exs`. - Params: None - Response: JSON, "ok" and 200 status -## `PATCH /api/pleroma/admin/users/confirm_email` +## `PATCH /api/v1/pleroma/admin/users/confirm_email` ### Confirm users' emails @@ -1152,7 +1170,7 @@ Loads json generated from `config/descriptions.exs`. - `nicknames` - Response: Array of user nicknames -## `PATCH /api/pleroma/admin/users/resend_confirmation_email` +## `PATCH /api/v1/pleroma/admin/users/resend_confirmation_email` ### Resend confirmation email @@ -1160,13 +1178,13 @@ Loads json generated from `config/descriptions.exs`. - `nicknames` - Response: Array of user nicknames -## `GET /api/pleroma/admin/stats` +## `GET /api/v1/pleroma/admin/stats` ### Stats - Query Params: - *optional* `instance`: **string** instance hostname (without protocol) to get stats for -- Example: `https://mypleroma.org/api/pleroma/admin/stats?instance=lain.com` +- Example: `https://mypleroma.org/api/v1/pleroma/admin/stats?instance=lain.com` - Response: @@ -1181,7 +1199,7 @@ Loads json generated from `config/descriptions.exs`. } ``` -## `GET /api/pleroma/admin/oauth_app` +## `GET /api/v1/pleroma/admin/oauth_app` ### List OAuth app @@ -1213,7 +1231,7 @@ Loads json generated from `config/descriptions.exs`. ``` -## `POST /api/pleroma/admin/oauth_app` +## `POST /api/v1/pleroma/admin/oauth_app` ### Create OAuth App @@ -1246,7 +1264,7 @@ Loads json generated from `config/descriptions.exs`. } ``` -## `PATCH /api/pleroma/admin/oauth_app/:id` +## `PATCH /api/v1/pleroma/admin/oauth_app/:id` ### Update OAuth App @@ -1271,7 +1289,7 @@ Loads json generated from `config/descriptions.exs`. } ``` -## `DELETE /api/pleroma/admin/oauth_app/:id` +## `DELETE /api/v1/pleroma/admin/oauth_app/:id` ### Delete OAuth App @@ -1282,7 +1300,7 @@ Loads json generated from `config/descriptions.exs`. - On failure: - 400 Bad Request `"Invalid parameters"` when `status` is missing -## `GET /api/pleroma/admin/media_proxy_caches` +## `GET /api/v1/pleroma/admin/media_proxy_caches` ### Get a list of all banned MediaProxy URLs in Cachex @@ -1306,7 +1324,7 @@ Loads json generated from `config/descriptions.exs`. ``` -## `POST /api/pleroma/admin/media_proxy_caches/delete` +## `POST /api/v1/pleroma/admin/media_proxy_caches/delete` ### Remove a banned MediaProxy URL from Cachex @@ -1321,7 +1339,7 @@ Loads json generated from `config/descriptions.exs`. ``` -## `POST /api/pleroma/admin/media_proxy_caches/purge` +## `POST /api/v1/pleroma/admin/media_proxy_caches/purge` ### Purge a MediaProxy URL @@ -1337,7 +1355,7 @@ Loads json generated from `config/descriptions.exs`. ``` -## GET /api/pleroma/admin/users/:nickname/chats +## GET /api/v1/pleroma/admin/users/:nickname/chats ### List a user's chats @@ -1366,7 +1384,7 @@ Loads json generated from `config/descriptions.exs`. ] ``` -## GET /api/pleroma/admin/chats/:chat_id +## GET /api/v1/pleroma/admin/chats/:chat_id ### View a single chat @@ -1393,7 +1411,7 @@ Loads json generated from `config/descriptions.exs`. } ``` -## GET /api/pleroma/admin/chats/:chat_id/messages +## GET /api/v1/pleroma/admin/chats/:chat_id/messages ### List the messages in a chat @@ -1431,7 +1449,7 @@ Loads json generated from `config/descriptions.exs`. ] ``` -## DELETE /api/pleroma/admin/chats/:chat_id/messages/:message_id +## DELETE /api/v1/pleroma/admin/chats/:chat_id/messages/:message_id ### Delete a single message @@ -1458,7 +1476,7 @@ Loads json generated from `config/descriptions.exs`. } ``` -## `GET /api/pleroma/admin/instance_document/:document_name` +## `GET /api/v1/pleroma/admin/instance_document/:document_name` ### Get an instance document @@ -1472,7 +1490,7 @@ Returns the content of the document

Instance panel

``` -## `PATCH /api/pleroma/admin/instance_document/:document_name` +## `PATCH /api/v1/pleroma/admin/instance_document/:document_name` - Params: - `file` (the file to be uploaded, using multipart form data.) @@ -1488,7 +1506,7 @@ Returns the content of the document } ``` -## `DELETE /api/pleroma/admin/instance_document/:document_name` +## `DELETE /api/v1/pleroma/admin/instance_document/:document_name` ### Delete an instance document @@ -1500,7 +1518,7 @@ Returns the content of the document } ``` -## `GET /api/pleroma/admin/frontends +## `GET /api/v1/pleroma/admin/frontends ### List available frontends @@ -1525,7 +1543,7 @@ Returns the content of the document ] ``` -## `POST /api/pleroma/admin/frontends/install` +## `POST /api/v1/pleroma/admin/frontends/install` ### Install a frontend diff --git a/docs/API/chats.md b/docs/development/API/chats.md similarity index 100% rename from docs/API/chats.md rename to docs/development/API/chats.md diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/development/API/differences_in_mastoapi_responses.md similarity index 88% rename from docs/API/differences_in_mastoapi_responses.md rename to docs/development/API/differences_in_mastoapi_responses.md index 1b197e073..6c1ecb559 100644 --- a/docs/API/differences_in_mastoapi_responses.md +++ b/docs/development/API/differences_in_mastoapi_responses.md @@ -16,6 +16,12 @@ Adding the parameter `reply_visibility` to the public and home timelines queries Adding the parameter `instance=lain.com` to the public timeline will show only statuses originating from `lain.com` (or any remote instance). +Home, public, hashtag & list timelines accept these parameters: + +- `only_media`: show only statuses with media attached +- `local`: show only local statuses +- `remote`: show only remote statuses + ## Statuses - `visibility`: has additional possible values `list` and `local` (for local-only statuses) @@ -32,6 +38,13 @@ Has these additional fields under the `pleroma` object: - `thread_muted`: true if the thread the post belongs to is muted - `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint. - `parent_visible`: If the parent of this post is visible to the user or not. +- `pinned_at`: a datetime (iso8601) when status was pinned, `null` otherwise. + +## Scheduled statuses + +Has these additional fields in `params`: + +- `expires_in`: the number of seconds the posted activity should expire in. ## Media Attachments @@ -54,6 +67,23 @@ The `id` parameter can also be the `nickname` of the user. This only works in th - `/api/v1/accounts/:id` - `/api/v1/accounts/:id/statuses` +`/api/v1/accounts/:id/statuses` endpoint accepts these parameters: + +- `pinned`: include only pinned statuses +- `tagged`: with tag +- `only_media`: include only statuses with media attached +- `with_muted`: include statuses/reactions from muted accounts +- `exclude_reblogs`: exclude reblogs +- `exclude_replies`: exclude replies +- `exclude_visibilities`: exclude visibilities + +Endpoints which accept `with_relationships` parameter: + +- `/api/v1/accounts/:id` +- `/api/v1/accounts/:id/followers` +- `/api/v1/accounts/:id/following` +- `/api/v1/mutes` + Has these additional fields under the `pleroma` object: - `ap_id`: nullable URL string, ActivityPub id of the user @@ -69,12 +99,12 @@ Has these additional fields under the `pleroma` object: - `hide_followers_count`: boolean, true when the user has follower stat hiding enabled - `hide_follows_count`: boolean, true when the user has follow stat hiding enabled - `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `/api/v1/accounts/verify_credentials` and `/api/v1/accounts/update_credentials` -- `chat_token`: The token needed for Pleroma chat. Only returned in `/api/v1/accounts/verify_credentials` +- `chat_token`: The token needed for Pleroma shoutbox. Only returned in `/api/v1/accounts/verify_credentials` - `deactivated`: boolean, true when the user is deactivated - `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts - `unread_conversation_count`: The count of unread conversations. Only returned to the account owner. - `unread_notifications_count`: The count of unread notifications. Only returned to the account owner. -- `notification_settings`: object, can be absent. See `/api/pleroma/notification_settings` for the parameters/keys returned. +- `notification_settings`: object, can be absent. See `/api/v1/pleroma/notification_settings` for the parameters/keys returned. - `accepts_chat_messages`: boolean, but can be null if we don't have that information about a user - `favicon`: nullable URL string, Favicon image of the user's instance @@ -206,6 +236,7 @@ Additional parameters can be added to the JSON body/Form data: - `pleroma_settings_store` - Opaque user settings to be saved on the backend. - `skip_thread_containment` - if true, skip filtering out broken threads - `allow_following_move` - if true, allows automatically follow moved following accounts +- `also_known_as` - array of ActivityPub IDs, needed for following move - `pleroma_background_image` - sets the background image of the user. Can be set to "" (an empty string) to reset. - `discoverable` - if true, external services (search bots) etc. are allowed to index / list the account (regardless of this setting, user will still appear in regular search results). - `actor_type` - the type of this account. @@ -225,9 +256,29 @@ This information is returned in the `/api/v1/accounts/verify_credentials` endpoi *Pleroma supports refreshing tokens.* -`POST /oauth/token` +### POST `/oauth/token` -Post here request with `grant_type=refresh_token` to obtain new access token. Returns an access token. +You can obtain access tokens for a user in a few additional ways. + +#### Refreshing a token + +To obtain a new access token from a refresh token, pass `grant_type=refresh_token` with the following extra parameters: + +- `refresh_token`: The refresh token. + +#### Getting a token with a password + +To obtain a token from a user's password, pass `grant_type=password` with the following extra parameters: + +- `username`: Username to authenticate. +- `password`: The user's password. + +#### Response body + +Additional fields are returned in the response: + +- `id`: The primary key of this token in Pleroma's database. +- `me` (user tokens only): The ActivityPub ID of the user who owns the token. ## Account Registration diff --git a/docs/API/pleroma_api.md b/docs/development/API/pleroma_api.md similarity index 93% rename from docs/API/pleroma_api.md rename to docs/development/API/pleroma_api.md index d8790ca32..d896f0ce7 100644 --- a/docs/API/pleroma_api.md +++ b/docs/development/API/pleroma_api.md @@ -4,7 +4,9 @@ Requests that require it can be authenticated with [an OAuth token](https://tool Request parameters can be passed via [query strings](https://en.wikipedia.org/wiki/Query_string) or as [form data](https://www.w3.org/TR/html401/interact/forms.html). Files must be uploaded as `multipart/form-data`. -## `/api/pleroma/emoji` +The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/api/pleroma/*` will be deprecated in the future). + +## `/api/v1/pleroma/emoji` ### Lists the custom emoji on that server. * Method: `GET` * Authentication: not required @@ -35,7 +37,7 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi ``` * Note: Same data as Mastodon API’s `/api/v1/custom_emojis` but in a different format -## `/api/pleroma/follow_import` +## `/api/v1/pleroma/follow_import` ### Imports your follows, for example from a Mastodon CSV file. * Method: `POST` * Authentication: required @@ -44,7 +46,7 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi * Response: HTTP 200 on success, 500 on error * Note: Users that can't be followed are silently skipped. -## `/api/pleroma/blocks_import` +## `/api/v1/pleroma/blocks_import` ### Imports your blocks. * Method: `POST` * Authentication: required @@ -52,7 +54,7 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi * `list`: STRING or FILE containing a whitespace-separated list of accounts to block * Response: HTTP 200 on success, 500 on error -## `/api/pleroma/mutes_import` +## `/api/v1/pleroma/mutes_import` ### Imports your mutes. * Method: `POST` * Authentication: required @@ -60,7 +62,7 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi * `list`: STRING or FILE containing a whitespace-separated list of accounts to mute * Response: HTTP 200 on success, 500 on error -## `/api/pleroma/captcha` +## `/api/v1/pleroma/captcha` ### Get a new captcha * Method: `GET` * Authentication: not required @@ -68,7 +70,7 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi * Response: Provider specific JSON, the only guaranteed parameter is `type` * Example response: `{"type": "kocaptcha", "token": "whatever", "url": "https://captcha.kotobank.ch/endpoint", "seconds_valid": 300}` -## `/api/pleroma/delete_account` +## `/api/v1/pleroma/delete_account` ### Delete an account * Method `POST` * Authentication: required @@ -77,7 +79,7 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi * Response: JSON. Returns `{"status": "success"}` if the deletion was successful, `{"error": "[error message]"}` otherwise * Example response: `{"error": "Invalid password."}` -## `/api/pleroma/disable_account` +## `/api/v1/pleroma/disable_account` ### Disable an account * Method `POST` * Authentication: required @@ -86,21 +88,21 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi * Response: JSON. Returns `{"status": "success"}` if the account was successfully disabled, `{"error": "[error message]"}` otherwise * Example response: `{"error": "Invalid password."}` -## `/api/pleroma/accounts/mfa` +## `/api/v1/pleroma/accounts/mfa` #### Gets current MFA settings * method: `GET` * Authentication: required * OAuth scope: `read:security` * Response: JSON. Returns `{"enabled": "false", "totp": false }` -## `/api/pleroma/accounts/mfa/setup/totp` +## `/api/v1/pleroma/accounts/mfa/setup/totp` #### Pre-setup the MFA/TOTP method * method: `GET` * Authentication: required * OAuth scope: `write:security` * Response: JSON. Returns `{"key": [secret_key], "provisioning_uri": "[qr code uri]" }` when successful, otherwise returns HTTP 422 `{"error": "error_msg"}` -## `/api/pleroma/accounts/mfa/confirm/totp` +## `/api/v1/pleroma/accounts/mfa/confirm/totp` #### Confirms & enables MFA/TOTP support for user account. * method: `POST` * Authentication: required @@ -111,7 +113,7 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi * Response: JSON. Returns `{}` if the enable was successful, HTTP 422 `{"error": "[error message]"}` otherwise -## `/api/pleroma/accounts/mfa/totp` +## `/api/v1/pleroma/accounts/mfa/totp` #### Disables MFA/TOTP method for user account. * method: `DELETE` * Authentication: required @@ -121,14 +123,14 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi * Response: JSON. Returns `{}` if the disable was successful, HTTP 422 `{"error": "[error message]"}` otherwise * Example response: `{"error": "Invalid password."}` -## `/api/pleroma/accounts/mfa/backup_codes` +## `/api/v1/pleroma/accounts/mfa/backup_codes` #### Generstes backup codes MFA for user account. * method: `GET` * Authentication: required * OAuth scope: `write:security` * Response: JSON. Returns `{"codes": codes}`when successful, otherwise HTTP 422 `{"error": "[error message]"}` -## `/api/pleroma/admin/` +## `/api/v1/pleroma/admin/` See [Admin-API](admin_api.md) ## `/api/v1/pleroma/notifications/read` @@ -298,7 +300,7 @@ See [Admin-API](admin_api.md) * Note: Behaves exactly the same as `POST /api/v1/upload`. Can only accept images - any attempt to upload non-image files will be met with `HTTP 415 Unsupported Media Type`. -## `/api/pleroma/notification_settings` +## `/api/v1/pleroma/notification_settings` ### Updates user notification settings * Method `PUT` * Authentication: required @@ -307,7 +309,7 @@ See [Admin-API](admin_api.md) * `hide_notification_contents`: BOOLEAN field. When set to true, it removes the contents of a message from the push notification. * Response: JSON. Returns `{"status": "success"}` if the update was successful, otherwise returns `{"error": "error_msg"}` -## `/api/pleroma/healthcheck` +## `/api/v1/pleroma/healthcheck` ### Healthcheck endpoint with additional system data. * Method `GET` * Authentication: not required @@ -325,7 +327,7 @@ See [Admin-API](admin_api.md) } ``` -## `/api/pleroma/change_email` +## `/api/v1/pleroma/change_email` ### Change account email * Method `POST` * Authentication: required @@ -378,7 +380,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa * Params: None * Response: JSON, returns a list of Mastodon Conversation entities that were marked as read (200 - healthy, 503 unhealthy). -## `GET /api/pleroma/emoji/pack?name=:name` +## `GET /api/v1/pleroma/emoji/pack?name=:name` ### Get pack.json for the pack @@ -397,7 +399,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa } ``` -## `POST /api/pleroma/emoji/pack?name=:name` +## `POST /api/v1/pleroma/emoji/pack?name=:name` ### Creates an empty pack @@ -407,7 +409,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa * `name`: pack name * Response: JSON, "ok" and 200 status or 409 if the pack with that name already exists -## `PATCH /api/pleroma/emoji/pack?name=:name` +## `PATCH /api/v1/pleroma/emoji/pack?name=:name` ### Updates (replaces) pack metadata @@ -425,7 +427,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa * Response: JSON, updated "metadata" section of the pack and 200 status or 400 if there was a problem with the new metadata (the error is specified in the "error" part of the response JSON) -## `DELETE /api/pleroma/emoji/pack?name=:name` +## `DELETE /api/v1/pleroma/emoji/pack?name=:name` ### Delete a custom emoji pack @@ -435,7 +437,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa * `name`: pack name * Response: JSON, "ok" and 200 status or 500 if there was an error deleting the pack -## `GET /api/pleroma/emoji/packs/import` +## `GET /api/v1/pleroma/emoji/packs/import` ### Imports packs from filesystem @@ -444,7 +446,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa * Params: None * Response: JSON, returns a list of imported packs. -## `GET /api/pleroma/emoji/packs/remote` +## `GET /api/v1/pleroma/emoji/packs/remote` ### Make request to another instance for packs list @@ -456,7 +458,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa * `page_size`: page size for packs (default 50) * Response: JSON with the pack list, hashmap with pack name and pack contents -## `POST /api/pleroma/emoji/packs/download` +## `POST /api/v1/pleroma/emoji/packs/download` ### Download pack from another instance @@ -469,7 +471,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa * Response: JSON, "ok" with 200 status if the pack was downloaded, or 500 if there were errors downloading the pack -## `POST /api/pleroma/emoji/packs/files?name=:name` +## `POST /api/v1/pleroma/emoji/packs/files?name=:name` ### Add new file to the pack @@ -482,7 +484,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa * `filename`: (*optional*) new emoji file name. If not specified will be taken from original filename. * Response: JSON, list of files for updated pack (hashmap -> shortcode => filename) with status 200, either error status with error message. -## `PATCH /api/pleroma/emoji/packs/files?name=:name` +## `PATCH /api/v1/pleroma/emoji/packs/files?name=:name` ### Update emoji file from pack @@ -496,7 +498,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa * `force`: (*optional*) with true value to overwrite existing emoji with new shortcode * Response: JSON, list with updated files for updated pack (hashmap -> shortcode => filename) with status 200, either error status with error message. -## `DELETE /api/pleroma/emoji/packs/files?name=:name` +## `DELETE /api/v1/pleroma/emoji/packs/files?name=:name` ### Delete emoji file from pack @@ -507,7 +509,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa * `shortcode`: emoji file shortcode * Response: JSON, list with updated files for updated pack (hashmap -> shortcode => filename) with status 200, either error status with error message. -## `GET /api/pleroma/emoji/packs` +## `GET /api/v1/pleroma/emoji/packs` ### Lists local custom emoji packs @@ -528,7 +530,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa } ``` -## `GET /api/pleroma/emoji/packs/archive?name=:name` +## `GET /api/v1/pleroma/emoji/packs/archive?name=:name` ### Requests a local pack archive from the instance diff --git a/docs/API/prometheus.md b/docs/development/API/prometheus.md similarity index 100% rename from docs/API/prometheus.md rename to docs/development/API/prometheus.md diff --git a/docs/ap_extensions.md b/docs/development/ap_extensions.md similarity index 100% rename from docs/ap_extensions.md rename to docs/development/ap_extensions.md diff --git a/docs/dev.md b/docs/development/authentication_authorization.md similarity index 72% rename from docs/dev.md rename to docs/development/authentication_authorization.md index 765380a58..183bfc2c9 100644 --- a/docs/dev.md +++ b/docs/development/authentication_authorization.md @@ -1,5 +1,3 @@ -This document contains notes and guidelines for Pleroma developers. - # Authentication & Authorization ## OAuth token-based authentication & authorization @@ -20,27 +18,4 @@ This document contains notes and guidelines for Pleroma developers. ## Auth-related configuration, OAuth consumer mode etc. -See `Authentication` section of [the configuration cheatsheet](configuration/cheatsheet.md#authentication). - -## MRF policies descriptions - -If MRF policy depends on config, it can be added into MRF tab to adminFE by adding `config_description/0` method, which returns map with special structure. - -Example: - -```elixir -%{ - key: :mrf_activity_expiration, - related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy", - label: "MRF Activity Expiration Policy", - description: "Adds automatic expiration to all local activities", - children: [ - %{ - key: :days, - type: :integer, - description: "Default global expiration time for all local activities (in days)", - suggestions: [90, 365] - } - ] - } -``` +See `Authentication` section of [the configuration cheatsheet](../configuration/cheatsheet.md#authentication). diff --git a/docs/development/index.md b/docs/development/index.md new file mode 100644 index 000000000..01a617596 --- /dev/null +++ b/docs/development/index.md @@ -0,0 +1 @@ +This section contains notes and guidelines for developers. diff --git a/docs/development/setting_up_pleroma_dev.md b/docs/development/setting_up_pleroma_dev.md new file mode 100644 index 000000000..8da761d62 --- /dev/null +++ b/docs/development/setting_up_pleroma_dev.md @@ -0,0 +1,70 @@ +# Setting up a Pleroma development environment + +Pleroma requires some adjustments from the defaults for running the instance locally. The following should help you to get started. + +## Installing + +1. Install Pleroma as explained in [the docs](../installation/debian_based_en.md), with some exceptions: + * You can use your own fork of the repository and add pleroma as a remote `git remote add pleroma 'https://git.pleroma.social/pleroma/pleroma'` + * You can skip systemd and nginx and all that stuff + * No need to create a dedicated pleroma user, it's easier to just use your own user + * For the DB you can still choose a dedicated user, the mix tasks set it up for you so it's no extra work for you + * For domain you can use `localhost` + * instead of creating a `prod.secret.exs`, create `dev.secret.exs` + * No need to prefix with `MIX_ENV=prod`. We're using dev and that's the default MIX_ENV +2. Change the dev.secret.exs + * Change the scheme in `config :pleroma, Pleroma.Web.Endpoint` to http (see examples below) + * If you want to change other settings, you can do that too +3. You can now start the server `mix phx.server`. Once it's build and started, you can access the instance on `http://:` (e.g.http://localhost:4000 ) and should be able to do everything locally you normaly can. + +Example config to change the scheme to http. Change the port if you want to run on another port. +```elixir + config :pleroma, Pleroma.Web.Endpoint, + url: [host: "localhost", scheme: "http", port: 4000], +``` + +Example config to disable captcha. This makes it a bit easier to create test-users. +```elixir +config :pleroma, Pleroma.Captcha, + enabled: false +``` + +Example config to change the log level to info +```elixir +config :logger, :console, + # :debug :info :warning :error + level: :info +``` + +## Testing + +1. Create a `test.secret.exs` file with the content as shown below +2. Create the database user and test database. + 1. You can use the `config/setup_db.psql` as a template. Copy the file if you want and change the database name, user and password to the values for the test-database (e.g. 'pleroma_local_test' for database and user). Then run this file like you did during installation. + 2. The tests will try to create the Database, so we'll have to allow our test-database user to create databases, `sudo -Hu postgres psql -c "ALTER USER pleroma_local_test WITH CREATEDB;"` +3. Run the tests with `mix test`. The tests should succeed. + +Example content for the `test.secret.exs` file. Feel free to use another user, database name or password, just make sure the database is dedicated for the testing environment. +```elixir +# Pleroma test configuration + +# NOTE: This file should not be committed to a repo or otherwise made public +# without removing sensitive information. + +import Config + +config :pleroma, Pleroma.Repo, + username: "pleroma_local_test", + password: "mysuperduperpassword", + database: "pleroma_local_test", + hostname: "localhost" + +``` + +## Updating + +Update Pleroma as explained in [the docs](../administration/updating.md). Just make sure you pull from upstream and not from your own fork. + +## Working on multiple branches + +If you develop on a separate branch, it's possible you did migrations that aren't merged into another branch you're working on. If you have multiple things you're working on, it's probably best to set up multiple pleroma's each with their own database. If you finished with a branch and want to switch back to develop to start a new branch from there, you can drop the database and recreate the database (e.g. by using `config/setup_db.psql`). The commands to drop and recreate the database can be found in [the docs](../administration/backup.md). diff --git a/docs/index.md b/docs/index.md index 1a90d0a8d..80c5d2631 100644 --- a/docs/index.md +++ b/docs/index.md @@ -20,7 +20,7 @@ The default front-end used by Pleroma is Pleroma-FE. You can find more informati ### Mastodon interface If the Pleroma interface isn't your thing, or you're just trying something new but you want to keep using the familiar Mastodon interface, we got that too! -Just add a "/web" after your instance url (e.g. ) and you'll end on the Mastodon web interface, but with a Pleroma backend! MAGIC! +Just add a "/web" after your instance url (e.g. ) and you'll end on the Mastodon web interface, but with a Pleroma backend! MAGIC! The Mastodon interface is from the Glitch-soc fork. For more information on the Mastodon interface you can check the [Mastodon](https://docs.joinmastodon.org/) and [Glitch-soc](https://glitch-soc.github.io/docs/) documentation. Remember, what you see is only the frontend part of Mastodon, the backend is still Pleroma. diff --git a/docs/installation/alpine_linux_en.md b/docs/installation/alpine_linux_en.md index 62f2fb778..7eb1718f2 100644 --- a/docs/installation/alpine_linux_en.md +++ b/docs/installation/alpine_linux_en.md @@ -80,7 +80,7 @@ sudo /etc/init.d/postgresql start sudo rc-update add postgresql ``` -### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md)) +### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md)) ```shell sudo apk add ffmpeg imagemagick exiftool @@ -125,7 +125,7 @@ sudo -Hu pleroma mix deps.get * Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances): ```shell -mv config/{generated_config.exs,prod.secret.exs} +sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs} ``` * The previous command creates also the file `config/setup_db.psql`, with which you can create the database: diff --git a/docs/installation/arch_linux_en.md b/docs/installation/arch_linux_en.md index 0eb6d2d5f..da78c3205 100644 --- a/docs/installation/arch_linux_en.md +++ b/docs/installation/arch_linux_en.md @@ -56,7 +56,7 @@ sudo -iu postgres initdb -D /var/lib/postgres/data sudo systemctl enable --now postgresql.service ``` -### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md)) +### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md)) ```shell sudo pacman -S ffmpeg imagemagick perl-image-exiftool @@ -100,7 +100,7 @@ sudo -Hu pleroma mix deps.get * Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances): ```shell -mv config/{generated_config.exs,prod.secret.exs} +sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs} ``` * The previous command creates also the file `config/setup_db.psql`, with which you can create the database: diff --git a/docs/installation/debian_based_en.md b/docs/installation/debian_based_en.md index 2b1c7406f..c5687a01e 100644 --- a/docs/installation/debian_based_en.md +++ b/docs/installation/debian_based_en.md @@ -54,7 +54,7 @@ sudo apt update sudo apt install elixir erlang-dev erlang-nox ``` -### Optional packages: [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md) +### Optional packages: [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md) ```shell sudo apt install imagemagick ffmpeg libimage-exiftool-perl @@ -98,7 +98,7 @@ sudo -Hu pleroma mix deps.get * Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances): ```shell -mv config/{generated_config.exs,prod.secret.exs} +sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs} ``` diff --git a/docs/installation/debian_based_jp.md b/docs/installation/debian_based_jp.md index 94e22325c..c4bbd4780 100644 --- a/docs/installation/debian_based_jp.md +++ b/docs/installation/debian_based_jp.md @@ -54,7 +54,7 @@ sudo apt update sudo apt install elixir erlang-dev erlang-nox ``` -### オプションパッケージ: [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md) +### オプションパッケージ: [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md) ```shell sudo apt install imagemagick ffmpeg libimage-exiftool-perl @@ -98,7 +98,7 @@ sudo -Hu pleroma mix pleroma.instance gen * コンフィギュレーションを確認して、もし問題なければ、ファイル名を変更してください。 ``` -mv config/{generated_config.exs,prod.secret.exs} +sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs} ``` * 先程のコマンドで、すでに `config/setup_db.psql` というファイルが作られています。このファイルをもとに、データベースを作成します。 diff --git a/docs/installation/freebsd_en.md b/docs/installation/freebsd_en.md index fdcb06c53..2dc466eb8 100644 --- a/docs/installation/freebsd_en.md +++ b/docs/installation/freebsd_en.md @@ -26,7 +26,7 @@ Setup the required services to automatically start at boot, using `sysrc(8)`. # service postgresql start ``` -### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md)) +### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md)) ```shell # pkg install imagemagick ffmpeg p5-Image-ExifTool diff --git a/docs/installation/netbsd_en.md b/docs/installation/netbsd_en.md index d5fa04fdf..233cf28b7 100644 --- a/docs/installation/netbsd_en.md +++ b/docs/installation/netbsd_en.md @@ -44,7 +44,7 @@ pgsql=YES First, run `# /etc/rc.d/pgsql start`. Then, `$ sudo -Hu pgsql -g pgsql createdb`. -### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md)) +### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md)) `# pkgin install ImageMagick ffmpeg4 p5-Image-ExifTool` diff --git a/docs/installation/openbsd_en.md b/docs/installation/openbsd_en.md index 8092ac379..0e1269ca5 100644 --- a/docs/installation/openbsd_en.md +++ b/docs/installation/openbsd_en.md @@ -27,7 +27,7 @@ Pleroma requires a reverse proxy, OpenBSD has relayd in base (and is used in thi #### Optional software -Per [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md): +Per [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md): * ImageMagick * ffmpeg * exiftool diff --git a/docs/installation/openbsd_fi.md b/docs/installation/openbsd_fi.md index 01cf34ab4..a61434147 100644 --- a/docs/installation/openbsd_fi.md +++ b/docs/installation/openbsd_fi.md @@ -20,7 +20,7 @@ Asenna tarvittava ohjelmisto: #### Optional software -[`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md): +[`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md): * ImageMagick * ffmpeg * exiftool diff --git a/docs/installation/otp_en.md b/docs/installation/otp_en.md index 63eda63ca..13f9636f3 100644 --- a/docs/installation/otp_en.md +++ b/docs/installation/otp_en.md @@ -89,6 +89,8 @@ RUM indexes are an alternative indexing scheme that is not included in PostgreSQ #### (Optional) Performance configuration It is encouraged to check [Optimizing your PostgreSQL performance](../configuration/postgresql.md) document, for tips on PostgreSQL tuning. +Restart PostgreSQL to apply configuration changes: + === "Alpine" ``` rc-service postgresql restart @@ -99,17 +101,6 @@ It is encouraged to check [Optimizing your PostgreSQL performance](../configurat systemctl restart postgresql ``` -If you are using PostgreSQL 12 or higher, add this to your Ecto database configuration - -```elixir -# -config :pleroma, Pleroma.Repo, -prepare: :named, -parameters: [ - plan_cache_mode: "force_custom_plan" -] -``` - ### Installing Pleroma ```sh # Create a Pleroma user @@ -159,7 +150,7 @@ su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate" # su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate --migrations-path priv/repo/optional_migrations/rum_indexing/" # Start the instance to verify that everything is working as expected -su pleroma -s $SHELL -lc "export $(cat /opt/pleroma/config/pleroma.env); ./bin/pleroma daemon" +su pleroma -s $SHELL -lc "./bin/pleroma daemon" # Wait for about 20 seconds and query the instance endpoint, if it shows your uri, name and email correctly, you are configured correctly sleep 20 && curl http://localhost:4000/api/v1/instance @@ -299,7 +290,7 @@ nginx -t ## Create your first user and set as admin ```sh -cd /opt/pleroma/bin +cd /opt/pleroma su pleroma -s $SHELL -lc "./bin/pleroma_ctl user new joeuser joeuser@sld.tld --admin" ``` This will create an account withe the username of 'joeuser' with the email address of joeuser@sld.tld, and set that user's account as an admin. This will result in a link that you can paste into the browser, which logs you in and enables you to set the password. diff --git a/installation/apache-cache-purge.sh.example b/installation/apache-cache-purge.sh.example new file mode 100755 index 000000000..7b4262875 --- /dev/null +++ b/installation/apache-cache-purge.sh.example @@ -0,0 +1,36 @@ +#!/bin/sh + +# A simple shell script to delete a media from Apache's mod_disk_cache. +# You will likely need to setup a sudo rule like the following: +# +# Cmnd_Alias HTCACHECLEAN = /usr/local/sbin/htcacheclean +# pleroma ALL=HTCACHECLEAN, NOPASSWD: HTCACHECLEAN +# +# Please also ensure you have enabled: +# +# config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Script, url_format: :htcacheclean +# +# which will correctly format the URLs passed to this script for the htcacheclean utility. +# + +SCRIPTNAME=${0##*/} + +# mod_disk_cache directory +CACHE_DIRECTORY="/tmp/pleroma-media-cache" + +## Removes an item via the htcacheclean utility +## $1 - the filename, can be a pattern . +## $2 - the cache directory. +purge_item() { + sudo htcacheclean -v -p "${2}" "${1}" +} # purge_item + +purge() { + for url in $@ + do + echo "$SCRIPTNAME delete \`$url\` from cache ($CACHE_DIRECTORY)" + purge_item "$url" $CACHE_DIRECTORY + done +} + +purge $@ diff --git a/installation/download-mastofe-build.sh b/installation/download-mastofe-build.sh index ee9e1c217..ee353c48c 100755 --- a/installation/download-mastofe-build.sh +++ b/installation/download-mastofe-build.sh @@ -1,6 +1,6 @@ #!/bin/sh # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only project_id="74" project_branch="rebase/glitch-soc" @@ -9,29 +9,32 @@ static_dir="instance/static" # project_branch="pleroma" # static_dir="priv/static" -if [[ ! -d "${static_dir}" ]] +if [ ! -d "${static_dir}" ] then echo "Error: ${static_dir} directory is missing, are you sure you are running this script at the root of pleroma’s repository?" exit 1 fi -last_modified="$(curl -s -I 'https://git.pleroma.social/api/v4/projects/'${project_id}'/jobs/artifacts/'${project_branch}'/download?job=build' | grep '^Last-Modified:' | cut -d: -f2-)" +last_modified="$(curl --fail -s -I 'https://git.pleroma.social/api/v4/projects/'${project_id}'/jobs/artifacts/'${project_branch}'/download?job=build' | grep '^Last-Modified:' | cut -d: -f2-)" echo "branch:${project_branch}" echo "Last-Modified:${last_modified}" artifact="mastofe.zip" -if [[ -e mastofe.timestamp ]] && [[ "${last_modified}" != "" ]] +if [ "${last_modified}x" = "x" ] then - if [[ "$(cat mastofe.timestamp)" == "${last_modified}" ]] - then - echo "MastoFE is up-to-date, exiting…" - exit 0 - fi + echo "ERROR: Couldn't get the modification date of the latest build archive, maybe it expired, exiting..." + exit 1 fi -curl -c - "https://git.pleroma.social/api/v4/projects/${project_id}/jobs/artifacts/${project_branch}/download?job=build" -o "${artifact}" || exit +if [ -e mastofe.timestamp ] && [ "$(cat mastofe.timestamp)" = "${last_modified}" ] +then + echo "MastoFE is up-to-date, exiting..." + exit 0 +fi + +curl --fail -c - "https://git.pleroma.social/api/v4/projects/${project_id}/jobs/artifacts/${project_branch}/download?job=build" -o "${artifact}" || exit # TODO: Update the emoji as well rm -fr "${static_dir}/sw.js" "${static_dir}/packs" || exit diff --git a/installation/pleroma-apache.conf b/installation/pleroma-apache.conf index 0d627f2d7..139abe9e1 100644 --- a/installation/pleroma-apache.conf +++ b/installation/pleroma-apache.conf @@ -1,73 +1,84 @@ -# default Apache site config for Pleroma -# -# needed modules: define headers proxy proxy_http proxy_wstunnel rewrite ssl -# optional modules: cache cache_disk +# Sample Apache config for Pleroma # # Simple installation instructions: -# 1. Install your TLS certificate, possibly using Let's Encrypt. -# 2. Replace 'example.tld' with your instance's domain wherever it appears. -# 3. This assumes a Debian style Apache config. Copy this file to -# /etc/apache2/sites-available/ and then add a symlink to it in -# /etc/apache2/sites-enabled/ by running 'a2ensite pleroma-apache.conf', then restart Apache. +# 1. Install your TLS certificate. We recommend using Let's Encrypt via Certbot +# 2. Replace 'example.tld' with your instance's domain. +# 3. This assumes a Debian-style Apache config. Copy this file to +# /etc/apache2/sites-available/ and then activate the site by running +# 'a2ensite pleroma-apache.conf', then restart Apache. # # Optional: enable disk-based caching for the media proxy # For details, see https://git.pleroma.social/pleroma/pleroma/wikis/How%20to%20activate%20mediaproxy # -# 1. Create the directory listed below as the CacheRoot, and make sure +# 1. Create a directory as shown below for the CacheRoot and make sure # the Apache user can write to it. # 2. Configure Apache's htcacheclean to clean the directory periodically. -# 3. Run 'a2enmod cache cache_disk' and restart Apache. +# Your OS may provide a service you can enable to do this automatically. Define servername example.tld + + LoadModule proxy_module libexec/apache24/mod_proxy.so + + + LoadModule proxy_http_module libexec/apache24/mod_proxy_http.so + + + LoadModule proxy_wstunnel_module libexec/apache24/mod_proxy_wstunnel.so + + + LoadModule rewrite_module libexec/apache24/mod_rewrite.so + + + LoadModule ssl_module libexec/apache24/mod_ssl.so + + + LoadModule cache_module libexec/apache24/mod_cache.so + + + LoadModule cache_disk_module libexec/apache24/mod_cache_disk.so + + ServerName ${servername} ServerTokens Prod -ErrorLog ${APACHE_LOG_DIR}/error.log -CustomLog ${APACHE_LOG_DIR}/access.log combined +# If you want Pleroma-specific logs +#ErrorLog /var/log/httpd-pleroma-error.log +#CustomLog /var/log/httpd-pleroma-access.log combined - Redirect permanent / https://${servername} + RewriteEngine on + RewriteCond %{SERVER_NAME} =${servername} + RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent] SSLEngine on SSLCertificateFile /etc/letsencrypt/live/${servername}/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/${servername}/privkey.pem + # Make sure you have the certbot-apache module installed + Include /etc/letsencrypt/options-ssl-apache.conf - # Mozilla modern configuration, tweak to your needs - SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 - SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 - SSLHonorCipherOrder on - SSLCompression off - SSLSessionTickets off - - # uncomment the following to enable mediaproxy caching on disk - # - # CacheRoot /var/cache/apache2/mod_cache_disk - # CacheDirLevels 1 - # CacheDirLength 2 - # CacheEnable disk /proxy - # CacheLock on - # + # Uncomment the following to enable MediaProxy caching on disk + #CacheRoot /tmp/pleroma-media-cache/ + #CacheDirLevels 1 + #CacheDirLength 2 + #CacheEnable disk /proxy + #CacheLock on + #CacheHeader on + #CacheDetailHeader on + ## 16MB max filesize for caching, configure as desired + #CacheMaxFileSize 16000000 + #CacheDefaultExpire 86400 RewriteEngine On RewriteCond %{HTTP:Connection} Upgrade [NC] RewriteCond %{HTTP:Upgrade} websocket [NC] - RewriteRule /(.*) ws://localhost:4000/$1 [P,L] + RewriteRule /(.*) ws://127.0.0.1:4000/$1 [P,L] + #ProxyRequests must be off or you open your server to abuse as an open proxy ProxyRequests off - # this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only - # and `localhost.` resolves to [::0] on some systems: see issue #930 ProxyPass / http://127.0.0.1:4000/ ProxyPassReverse / http://127.0.0.1:4000/ - - RequestHeader set Host ${servername} ProxyPreserveHost On - -# OCSP Stapling, only in httpd 2.3.3 and later -SSLUseStapling on -SSLStaplingResponderTimeout 5 -SSLStaplingReturnResponderErrors off -SSLStaplingCache shmcb:/var/run/ocsp(128000) diff --git a/installation/pleroma.vcl b/installation/pleroma.vcl index 13dad784c..4752510ea 100644 --- a/installation/pleroma.vcl +++ b/installation/pleroma.vcl @@ -59,6 +59,13 @@ sub vcl_backend_response { set beresp.http.CR = beresp.http.content-range; } + # Bypass cache for large files + # 50000000 ~ 50MB + if (std.integer(beresp.http.content-length, 0) > 50000000) { + set beresp.uncacheable = true; + return(deliver); + } + # Don't cache objects that require authentication if (beresp.http.Authorization && !beresp.http.Cache-Control ~ "public") { set beresp.uncacheable = true; diff --git a/lib/jason_types.ex b/lib/jason_types.ex deleted file mode 100644 index f1fdc96f4..000000000 --- a/lib/jason_types.ex +++ /dev/null @@ -1,9 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -Postgrex.Types.define( - Pleroma.PostgresTypes, - [] ++ Ecto.Adapters.Postgres.extensions(), - json: Jason -) diff --git a/lib/mix/pleroma.ex b/lib/mix/pleroma.ex index 7575f0ef8..2b6c7d6bb 100644 --- a/lib/mix/pleroma.ex +++ b/lib/mix/pleroma.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Pleroma do @@ -12,7 +12,9 @@ defmodule Mix.Pleroma do :cachex, :flake_id, :swoosh, - :timex + :timex, + :fast_html, + :oban ] @cachex_children ["object", "user", "scrubber", "web_resp"] @doc "Common functions to be reused in mix tasks" @@ -37,12 +39,23 @@ def start_pleroma do Enum.each(apps, &Application.ensure_all_started/1) + oban_config = [ + crontab: [], + repo: Pleroma.Repo, + log: false, + queues: [], + plugins: [] + ] + children = [ Pleroma.Repo, + Pleroma.Emoji, {Pleroma.Config.TransferTask, false}, Pleroma.Web.Endpoint, - {Oban, Pleroma.Config.get(Oban)} + {Oban, oban_config}, + {Majic.Pool, + [name: Pleroma.MajicPool, pool_size: Pleroma.Config.get([:majic_pool, :size], 2)]} ] ++ http_children(adapter) diff --git a/lib/mix/tasks/pleroma/app.ex b/lib/mix/tasks/pleroma/app.ex index 463e2449f..0bf7ffabc 100644 --- a/lib/mix/tasks/pleroma/app.ex +++ b/lib/mix/tasks/pleroma/app.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.App do diff --git a/lib/mix/tasks/pleroma/benchmark.ex b/lib/mix/tasks/pleroma/benchmark.ex index a607d5d4f..fdf99747a 100644 --- a/lib/mix/tasks/pleroma/benchmark.ex +++ b/lib/mix/tasks/pleroma/benchmark.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Benchmark do diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex index d7e2e97e7..22502a522 100644 --- a/lib/mix/tasks/pleroma/config.ex +++ b/lib/mix/tasks/pleroma/config.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Config do @@ -27,7 +27,7 @@ def run(["migrate_from_db" | options]) do {opts, _} = OptionParser.parse!(options, - strict: [env: :string, delete: :boolean], + strict: [env: :string, delete: :boolean, path: :string], aliases: [d: :delete] ) @@ -259,18 +259,43 @@ defp create(group, settings) do defp migrate_from_db(opts) do env = opts[:env] || Pleroma.Config.get(:env) + filename = "#{env}.exported_from_db.secret.exs" + config_path = - if Pleroma.Config.get(:release) do - :config_path - |> Pleroma.Config.get() - |> Path.dirname() - else - "config" + cond do + opts[:path] -> + opts[:path] + + Pleroma.Config.get(:release) -> + :config_path + |> Pleroma.Config.get() + |> Path.dirname() + + true -> + "config" end - |> Path.join("#{env}.exported_from_db.secret.exs") + |> Path.join(filename) - file = File.open!(config_path, [:write, :utf8]) + with {:ok, file} <- File.open(config_path, [:write, :utf8]) do + write_config(file, config_path, opts) + shell_info("Database configuration settings have been exported to #{config_path}") + else + _ -> + shell_error("Impossible to save settings to this directory #{Path.dirname(config_path)}") + tmp_config_path = Path.join(System.tmp_dir!(), filename) + file = File.open!(tmp_config_path) + shell_info( + "Saving database configuration settings to #{tmp_config_path}. Copy it to the #{ + Path.dirname(config_path) + } manually." + ) + + write_config(file, tmp_config_path, opts) + end + end + + defp write_config(file, path, opts) do IO.write(file, config_header()) ConfigDB @@ -278,11 +303,7 @@ defp migrate_from_db(opts) do |> Enum.each(&write_and_delete(&1, file, opts[:delete])) :ok = File.close(file) - System.cmd("mix", ["format", config_path]) - - shell_info( - "Database configuration settings have been exported to config/#{env}.exported_from_db.secret.exs" - ) + System.cmd("mix", ["format", path]) end if Code.ensure_loaded?(Config.Reader) do diff --git a/lib/mix/tasks/pleroma/count_statuses.ex b/lib/mix/tasks/pleroma/count_statuses.ex index 8761d8f17..c29ea8567 100644 --- a/lib/mix/tasks/pleroma/count_statuses.ex +++ b/lib/mix/tasks/pleroma/count_statuses.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.CountStatuses do diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index a01c36ece..e7f4b67a4 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Database do @@ -8,10 +8,13 @@ defmodule Mix.Tasks.Pleroma.Database do alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User + require Logger require Pleroma.Constants + import Ecto.Query import Mix.Pleroma + use Mix.Task @shortdoc "A collection of database related tasks" @@ -48,9 +51,15 @@ def run(["bump_all_conversations"]) do def run(["update_users_following_followers_counts"]) do start_pleroma() - User - |> Repo.all() - |> Enum.each(&User.update_follower_count/1) + Repo.transaction( + fn -> + from(u in User, select: u) + |> Repo.stream() + |> Stream.each(&User.update_follower_count/1) + |> Stream.run() + end, + timeout: :infinity + ) end def run(["prune_objects" | args]) do @@ -161,4 +170,79 @@ def run(["ensure_expiration"]) do end) |> Stream.run() end + + def run(["set_text_search_config", tsconfig]) do + start_pleroma() + %{rows: [[tsc]]} = Ecto.Adapters.SQL.query!(Pleroma.Repo, "SHOW default_text_search_config;") + shell_info("Current default_text_search_config: #{tsc}") + + %{rows: [[db]]} = Ecto.Adapters.SQL.query!(Pleroma.Repo, "SELECT current_database();") + shell_info("Update default_text_search_config: #{tsconfig}") + + %{messages: msg} = + Ecto.Adapters.SQL.query!( + Pleroma.Repo, + "ALTER DATABASE #{db} SET default_text_search_config = '#{tsconfig}';" + ) + + # non-exist config will not raise excpetion but only give >0 messages + if length(msg) > 0 do + shell_info("Error: #{inspect(msg, pretty: true)}") + else + rum_enabled = Pleroma.Config.get([:database, :rum_enabled]) + shell_info("Recreate index, RUM: #{rum_enabled}") + + # Note SQL below needs to be kept up-to-date with latest GIN or RUM index definition in future + if rum_enabled do + Ecto.Adapters.SQL.query!( + Pleroma.Repo, + "CREATE OR REPLACE FUNCTION objects_fts_update() RETURNS trigger AS $$ BEGIN + new.fts_content := to_tsvector(new.data->>'content'); + RETURN new; + END + $$ LANGUAGE plpgsql" + ) + + shell_info("Refresh RUM index") + Ecto.Adapters.SQL.query!(Pleroma.Repo, "UPDATE objects SET updated_at = NOW();") + else + Ecto.Adapters.SQL.query!(Pleroma.Repo, "DROP INDEX IF EXISTS objects_fts;") + + Ecto.Adapters.SQL.query!( + Pleroma.Repo, + "CREATE INDEX objects_fts ON objects USING gin(to_tsvector('#{tsconfig}', data->>'content')); " + ) + end + + shell_info('Done.') + end + end + + # Rolls back a specific migration (leaving subsequent migrations applied). + # WARNING: imposes a risk of unrecoverable data loss — proceed at your own responsibility. + # Based on https://stackoverflow.com/a/53825840 + def run(["rollback", version]) do + prompt = "SEVERE WARNING: this operation may result in unrecoverable data loss. Continue?" + + if shell_prompt(prompt, "n") in ~w(Yn Y y) do + {_, result, _} = + Ecto.Migrator.with_repo(Pleroma.Repo, fn repo -> + version = String.to_integer(version) + re = ~r/^#{version}_.*\.exs/ + path = Ecto.Migrator.migrations_path(repo) + + with {_, "" <> file} <- {:find, Enum.find(File.ls!(path), &String.match?(&1, re))}, + {_, [{mod, _} | _]} <- {:compile, Code.compile_file(Path.join(path, file))}, + {_, :ok} <- {:rollback, Ecto.Migrator.down(repo, version, mod)} do + {:ok, "Reversed migration: #{file}"} + else + {:find, _} -> {:error, "No migration found with version prefix: #{version}"} + {:compile, e} -> {:error, "Problem compiling migration module: #{inspect(e)}"} + {:rollback, e} -> {:error, "Problem reversing migration: #{inspect(e)}"} + end + end) + + shell_info(inspect(result)) + end + end end diff --git a/lib/mix/tasks/pleroma/digest.ex b/lib/mix/tasks/pleroma/digest.ex index cac148b88..f34fc839e 100644 --- a/lib/mix/tasks/pleroma/digest.ex +++ b/lib/mix/tasks/pleroma/digest.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Digest do diff --git a/lib/mix/tasks/pleroma/docs.ex b/lib/mix/tasks/pleroma/docs.ex index ad5c37fc9..45cca1c74 100644 --- a/lib/mix/tasks/pleroma/docs.ex +++ b/lib/mix/tasks/pleroma/docs.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Docs do diff --git a/lib/mix/tasks/pleroma/ecto.ex b/lib/mix/tasks/pleroma/ecto.ex index 3363cd45f..69564c61a 100644 --- a/lib/mix/tasks/pleroma/ecto.ex +++ b/lib/mix/tasks/pleroma/ecto.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-onl defmodule Mix.Tasks.Pleroma.Ecto do diff --git a/lib/mix/tasks/pleroma/ecto/migrate.ex b/lib/mix/tasks/pleroma/ecto/migrate.ex index e903bd171..8d9f44e1c 100644 --- a/lib/mix/tasks/pleroma/ecto/migrate.ex +++ b/lib/mix/tasks/pleroma/ecto/migrate.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-onl defmodule Mix.Tasks.Pleroma.Ecto.Migrate do diff --git a/lib/mix/tasks/pleroma/ecto/rollback.ex b/lib/mix/tasks/pleroma/ecto/rollback.ex index 3dba952cb..025ebaf19 100644 --- a/lib/mix/tasks/pleroma/ecto/rollback.ex +++ b/lib/mix/tasks/pleroma/ecto/rollback.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-onl defmodule Mix.Tasks.Pleroma.Ecto.Rollback do @@ -20,7 +20,8 @@ defmodule Mix.Tasks.Pleroma.Ecto.Rollback do start: :boolean, quiet: :boolean, log_sql: :boolean, - migrations_path: :string + migrations_path: :string, + env: :string ] @moduledoc """ @@ -59,7 +60,7 @@ def run(args \\ []) do level = Logger.level() Logger.configure(level: :info) - if Pleroma.Config.get(:env) == :test do + if opts[:env] == "test" do Logger.info("Rollback succesfully") else {:ok, _, _} = diff --git a/lib/mix/tasks/pleroma/email.ex b/lib/mix/tasks/pleroma/email.ex index bc5facc09..4ce8c9b05 100644 --- a/lib/mix/tasks/pleroma/email.ex +++ b/lib/mix/tasks/pleroma/email.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Email do @@ -33,12 +33,12 @@ def run(["resend_confirmation_emails"]) do Pleroma.User.Query.build(%{ local: true, - deactivated: false, - confirmation_pending: true, + is_active: true, + is_confirmed: false, invisible: false }) |> Pleroma.Repo.chunk_stream(500) - |> Stream.each(&Pleroma.User.try_send_confirmation_email(&1)) + |> Stream.each(&Pleroma.User.maybe_send_confirmation_email(&1)) |> Stream.run() end end diff --git a/lib/mix/tasks/pleroma/emoji.ex b/lib/mix/tasks/pleroma/emoji.ex index 1750373f9..9ad4a7467 100644 --- a/lib/mix/tasks/pleroma/emoji.ex +++ b/lib/mix/tasks/pleroma/emoji.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Emoji do diff --git a/lib/mix/tasks/pleroma/frontend.ex b/lib/mix/tasks/pleroma/frontend.ex index f15dbc38b..8334e0049 100644 --- a/lib/mix/tasks/pleroma/frontend.ex +++ b/lib/mix/tasks/pleroma/frontend.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Frontend do diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index ac8688424..da27a99d0 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Instance do @@ -161,12 +161,21 @@ def run(["gen" | rest]) do ) |> Path.expand() + {strip_uploads_message, strip_uploads_default} = + if Pleroma.Utils.command_available?("exiftool") do + {"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as installed. (y/n)", + "y"} + else + {"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as not installed, please install it if you answer yes. (y/n)", + "n"} + end + strip_uploads = get_option( options, :strip_uploads, - "Do you want to strip location (GPS) data from uploaded images? (y/n)", - "y" + strip_uploads_message, + strip_uploads_default ) === "y" anonymize_uploads = @@ -233,6 +242,13 @@ def run(["gen" | rest]) do rum_enabled: rum_enabled ) + config_dir = Path.dirname(config_path) + psql_dir = Path.dirname(psql_path) + + [config_dir, psql_dir, static_dir, uploads_dir] + |> Enum.reject(&File.exists?/1) + |> Enum.map(&File.mkdir_p!/1) + shell_info("Writing config to #{config_path}.") File.write(config_path, result_config) @@ -253,7 +269,7 @@ def run(["gen" | rest]) do else shell_error( "The task would have overwritten the following files:\n" <> - (Enum.map(paths, &"- #{&1}\n") |> Enum.join("")) <> + (Enum.map(will_overwrite, &"- #{&1}\n") |> Enum.join("")) <> "Rerun with `--force` to overwrite them." ) end @@ -266,10 +282,6 @@ defp write_robots_txt(static_dir, indexable, template_dir) do indexable: indexable ) - unless File.exists?(static_dir) do - File.mkdir_p!(static_dir) - end - robots_txt_path = Path.join(static_dir, "robots.txt") if File.exists?(robots_txt_path) do diff --git a/lib/mix/tasks/pleroma/notification_settings.ex b/lib/mix/tasks/pleroma/notification_settings.ex index f99275de1..e16866b6a 100644 --- a/lib/mix/tasks/pleroma/notification_settings.ex +++ b/lib/mix/tasks/pleroma/notification_settings.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.NotificationSettings do diff --git a/lib/mix/tasks/pleroma/openapi_spec.ex b/lib/mix/tasks/pleroma/openapi_spec.ex new file mode 100644 index 000000000..8f719c58b --- /dev/null +++ b/lib/mix/tasks/pleroma/openapi_spec.ex @@ -0,0 +1,8 @@ +defmodule Mix.Tasks.Pleroma.OpenapiSpec do + def run([path]) do + # Load Pleroma application to get version info + Application.load(:pleroma) + spec = Pleroma.Web.ApiSpec.spec(server_specific: false) |> Jason.encode!() + File.write(path, spec) + end +end diff --git a/lib/mix/tasks/pleroma/refresh_counter_cache.ex b/lib/mix/tasks/pleroma/refresh_counter_cache.ex index efcbaa3b1..66eed8657 100644 --- a/lib/mix/tasks/pleroma/refresh_counter_cache.ex +++ b/lib/mix/tasks/pleroma/refresh_counter_cache.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.RefreshCounterCache do diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex index bb808ca47..01e6b4279 100644 --- a/lib/mix/tasks/pleroma/relay.ex +++ b/lib/mix/tasks/pleroma/relay.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Relay do diff --git a/lib/mix/tasks/pleroma/robots_txt.ex b/lib/mix/tasks/pleroma/robots_txt.ex index 24f08180e..2ae430761 100644 --- a/lib/mix/tasks/pleroma/robots_txt.ex +++ b/lib/mix/tasks/pleroma/robots_txt.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.RobotsTxt do diff --git a/lib/mix/tasks/pleroma/uploads.ex b/lib/mix/tasks/pleroma/uploads.ex index c47b7531e..333e9aa8e 100644 --- a/lib/mix/tasks/pleroma/uploads.ex +++ b/lib/mix/tasks/pleroma/uploads.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Uploads do diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index ca9c8579f..53d5fc6d9 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.User do @@ -74,7 +74,7 @@ def run(["new", nickname, email | rest]) do bio: bio } - changeset = User.register_changeset(%User{}, params, need_confirmation: false) + changeset = User.register_changeset(%User{}, params, is_confirmed: true) {:ok, _user} = User.register(changeset) shell_info("User #{nickname} created") @@ -107,21 +107,6 @@ def run(["rm", nickname]) do end end - def run(["toggle_activated", nickname]) do - start_pleroma() - - with %User{} = user <- User.get_cached_by_nickname(nickname) do - {:ok, user} = User.deactivate(user, !user.deactivated) - - shell_info( - "Activation status of #{nickname}: #{if(user.deactivated, do: "de", else: "")}activated" - ) - else - _ -> - shell_error("No user #{nickname}") - end - end - def run(["reset_password", nickname]) do start_pleroma() @@ -156,20 +141,41 @@ def run(["reset_mfa", nickname]) do end end + def run(["activate", nickname]) do + start_pleroma() + + with %User{} = user <- User.get_cached_by_nickname(nickname), + false <- user.is_active do + User.set_activation(user, true) + :timer.sleep(500) + + shell_info("Successfully activated #{nickname}") + else + true -> + shell_info("User #{nickname} already activated") + + _ -> + shell_error("No user #{nickname}") + end + end + def run(["deactivate", nickname]) do start_pleroma() - with %User{} = user <- User.get_cached_by_nickname(nickname) do - shell_info("Deactivating #{user.nickname}") - User.deactivate(user) + with %User{} = user <- User.get_cached_by_nickname(nickname), + true <- user.is_active do + User.set_activation(user, false) :timer.sleep(500) user = User.get_cached_by_id(user.id) if Enum.empty?(Enum.filter(User.get_friends(user), & &1.local)) do - shell_info("Successfully unsubscribed all local followers from #{user.nickname}") + shell_info("Successfully deactivated #{nickname} and unsubscribed all local followers") end else + false -> + shell_info("User #{nickname} already deactivated") + _ -> shell_error("No user #{nickname}") end @@ -213,7 +219,7 @@ def run(["set", nickname | rest]) do user = case Keyword.get(options, :confirmed) do nil -> user - value -> set_confirmed(user, value) + value -> set_confirmation(user, value) end user = @@ -345,13 +351,13 @@ def run(["delete_activities", nickname]) do end end - def run(["toggle_confirmed", nickname]) do + def run(["confirm", nickname]) do start_pleroma() with %User{} = user <- User.get_cached_by_nickname(nickname) do - {:ok, user} = User.toggle_confirmation(user) + {:ok, user} = User.confirm(user) - message = if user.confirmation_pending, do: "needs", else: "doesn't need" + message = if !user.is_confirmed, do: "needs", else: "doesn't need" shell_info("#{nickname} #{message} confirmation.") else @@ -365,7 +371,7 @@ def run(["confirm_all"]) do Pleroma.User.Query.build(%{ local: true, - deactivated: false, + is_active: true, is_moderator: false, is_admin: false, invisible: false @@ -373,7 +379,7 @@ def run(["confirm_all"]) do |> Pleroma.Repo.chunk_stream(500, :batches) |> Stream.each(fn users -> users - |> Enum.each(fn user -> User.need_confirmation(user, false) end) + |> Enum.each(fn user -> User.set_confirmation(user, true) end) end) |> Stream.run() end @@ -383,7 +389,7 @@ def run(["unconfirm_all"]) do Pleroma.User.Query.build(%{ local: true, - deactivated: false, + is_active: true, is_moderator: false, is_admin: false, invisible: false @@ -391,7 +397,7 @@ def run(["unconfirm_all"]) do |> Pleroma.Repo.chunk_stream(500, :batches) |> Stream.each(fn users -> users - |> Enum.each(fn user -> User.need_confirmation(user, true) end) + |> Enum.each(fn user -> User.set_confirmation(user, false) end) end) |> Stream.run() end @@ -420,7 +426,7 @@ def run(["list"]) do shell_info( "#{user.nickname} moderator: #{user.is_moderator}, admin: #{user.is_admin}, locked: #{ user.is_locked - }, deactivated: #{user.deactivated}" + }, is_active: #{user.is_active}" ) end) end) @@ -454,10 +460,10 @@ defp set_locked(user, value) do user end - defp set_confirmed(user, value) do - {:ok, user} = User.need_confirmation(user, !value) + defp set_confirmation(user, value) do + {:ok, user} = User.set_confirmation(user, value) - shell_info("Confirmation pending status of #{user.nickname}: #{user.confirmation_pending}") + shell_info("Confirmation status of #{user.nickname}: #{user.is_confirmed}") user end end diff --git a/lib/phoenix/transports/web_socket/raw.ex b/lib/phoenix/transports/web_socket/raw.ex index c3665bebe..8ed64eb16 100644 --- a/lib/phoenix/transports/web_socket/raw.ex +++ b/lib/phoenix/transports/web_socket/raw.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Phoenix.Transports.WebSocket.Raw do diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 079823312..53beca5e6 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity do @@ -24,6 +24,8 @@ defmodule Pleroma.Activity do @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true} + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + schema "activities" do field(:data, :map) field(:local, :boolean, default: true) @@ -111,6 +113,7 @@ def with_preloaded_bookmark(query, %User{} = user) do from([a] in query, left_join: b in Bookmark, on: b.user_id == ^user.id and b.activity_id == a.id, + as: :bookmark, preload: [bookmark: b] ) end @@ -121,6 +124,7 @@ def with_preloaded_report_notes(query) do from([a] in query, left_join: r in ReportNote, on: a.id == r.activity_id, + as: :report_note, preload: [report_notes: r] ) end @@ -180,40 +184,48 @@ def get_by_ap_id_with_object(ap_id) do |> Repo.one() end - @spec get_by_id(String.t()) :: Activity.t() | nil - def get_by_id(id) do - case FlakeId.flake_id?(id) do - true -> - Activity - |> where([a], a.id == ^id) - |> restrict_deactivated_users() - |> Repo.one() + @doc """ + Gets activity by ID, doesn't load activities from deactivated actors by default. + """ + @spec get_by_id(String.t(), keyword()) :: t() | nil + def get_by_id(id, opts \\ [filter: [:restrict_deactivated]]), do: get_by_id_with_opts(id, opts) - _ -> - nil + @spec get_by_id_with_user_actor(String.t()) :: t() | nil + def get_by_id_with_user_actor(id), do: get_by_id_with_opts(id, preload: [:user_actor]) + + @spec get_by_id_with_object(String.t()) :: t() | nil + def get_by_id_with_object(id), do: get_by_id_with_opts(id, preload: [:object]) + + defp get_by_id_with_opts(id, opts) do + if FlakeId.flake_id?(id) do + query = Queries.by_id(id) + + with_filters_query = + if is_list(opts[:filter]) do + Enum.reduce(opts[:filter], query, fn + {:type, type}, acc -> Queries.by_type(acc, type) + :restrict_deactivated, acc -> restrict_deactivated_users(acc) + _, acc -> acc + end) + else + query + end + + with_preloads_query = + if is_list(opts[:preload]) do + Enum.reduce(opts[:preload], with_filters_query, fn + :user_actor, acc -> with_preloaded_user_actor(acc) + :object, acc -> with_preloaded_object(acc) + _, acc -> acc + end) + else + with_filters_query + end + + Repo.one(with_preloads_query) end end - def get_by_id_with_user_actor(id) do - case FlakeId.flake_id?(id) do - true -> - Activity - |> where([a], a.id == ^id) - |> with_preloaded_user_actor() - |> Repo.one() - - _ -> - nil - end - end - - def get_by_id_with_object(id) do - Activity - |> where(id: ^id) - |> with_preloaded_object() - |> Repo.one() - end - def all_by_ids_with_object(ids) do Activity |> where([a], a.id in ^ids) @@ -265,6 +277,11 @@ def get_create_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do def get_create_by_object_ap_id_with_object(_), do: nil + @spec create_by_id_with_object(String.t()) :: t() | nil + def create_by_id_with_object(id) do + get_by_id_with_opts(id, preload: [:object], filter: [type: "Create"]) + end + defp get_in_reply_to_activity_from_object(%Object{data: %{"inReplyTo" => ap_id}}) do get_create_by_object_ap_id_with_object(ap_id) end @@ -272,7 +289,7 @@ defp get_in_reply_to_activity_from_object(%Object{data: %{"inReplyTo" => ap_id}} defp get_in_reply_to_activity_from_object(_), do: nil def get_in_reply_to_activity(%Activity{} = activity) do - get_in_reply_to_activity_from_object(Object.normalize(activity)) + get_in_reply_to_activity_from_object(Object.normalize(activity, fetch: false)) end def normalize(obj) when is_map(obj), do: get_by_ap_id_with_object(obj["id"]) @@ -298,7 +315,7 @@ def delete_all_by_object_ap_id(_), do: nil defp purge_web_resp_cache(%Activity{} = activity) do %{path: path} = URI.parse(activity.data["id"]) - Cachex.del(:web_resp_cache, path) + @cachex.del(:web_resp_cache, path) activity end @@ -364,12 +381,6 @@ def direct_conversation_id(activity, for_user) do end end - @spec pinned_by_actor?(Activity.t()) :: boolean() - def pinned_by_actor?(%Activity{} = activity) do - actor = user_actor(activity) - activity.id in actor.pinned_activities - end - @spec get_by_object_ap_id_with_object(String.t()) :: t() | nil def get_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do ap_id @@ -380,4 +391,13 @@ def get_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do end def get_by_object_ap_id_with_object(_), do: nil + + @spec add_by_params_query(String.t(), String.t(), String.t()) :: Ecto.Query.t() + def add_by_params_query(object_id, actor, target) do + object_id + |> Queries.by_object_id() + |> Queries.by_type("Add") + |> Queries.by_actor(actor) + |> where([a], fragment("?->>'target' = ?", a.data, ^target)) + end end diff --git a/lib/pleroma/activity/ir/topics.ex b/lib/pleroma/activity/ir/topics.ex index fe2e8cb5c..7a603a615 100644 --- a/lib/pleroma/activity/ir/topics.ex +++ b/lib/pleroma/activity/ir/topics.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity.Ir.Topics do @@ -8,7 +8,7 @@ defmodule Pleroma.Activity.Ir.Topics do def get_activity_topics(activity) do activity - |> Object.normalize() + |> Object.normalize(fetch: false) |> generate_topics(activity) |> List.flatten() end @@ -48,14 +48,12 @@ defp item_creation_tags(tags, _, _) do tags end - defp hashtags_to_topics(%{data: %{"tag" => tags}}) do - tags - |> Enum.filter(&is_bitstring(&1)) - |> Enum.map(fn tag -> "hashtag:" <> tag end) + defp hashtags_to_topics(object) do + object + |> Object.hashtags() + |> Enum.map(fn hashtag -> "hashtag:" <> hashtag end) end - defp hashtags_to_topics(_), do: [] - defp remote_topics(%{local: true}), do: [] defp remote_topics(%{actor: actor}) when is_binary(actor), diff --git a/lib/pleroma/activity/queries.ex b/lib/pleroma/activity/queries.ex index c99aae44b..4632651b0 100644 --- a/lib/pleroma/activity/queries.ex +++ b/lib/pleroma/activity/queries.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity.Queries do @@ -14,6 +14,11 @@ defmodule Pleroma.Activity.Queries do alias Pleroma.Activity alias Pleroma.User + @spec by_id(query(), String.t()) :: query() + def by_id(query \\ Activity, id) do + from(a in query, where: a.id == ^id) + end + @spec by_ap_id(query, String.t()) :: query def by_ap_id(query \\ Activity, ap_id) do from( diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex index babf9520b..ed898ba4f 100644 --- a/lib/pleroma/activity/search.ex +++ b/lib/pleroma/activity/search.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity.Search do @@ -64,7 +64,7 @@ defp query_with(q, :gin, search_query, :plain) do from([a, o] in q, where: fragment( - "to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)", + "to_tsvector(?->>'content') @@ plainto_tsquery(?)", o.data, ^search_query ) @@ -75,7 +75,7 @@ defp query_with(q, :gin, search_query, :websearch) do from([a, o] in q, where: fragment( - "to_tsvector('english', ?->>'content') @@ websearch_to_tsquery('english', ?)", + "to_tsvector(?->>'content') @@ websearch_to_tsquery(?)", o.data, ^search_query ) @@ -86,7 +86,7 @@ defp query_with(q, :rum, search_query, :plain) do from([a, o] in q, where: fragment( - "? @@ plainto_tsquery('english', ?)", + "? @@ plainto_tsquery(?)", o.fts_content, ^search_query ), @@ -98,7 +98,7 @@ defp query_with(q, :rum, search_query, :websearch) do from([a, o] in q, where: fragment( - "? @@ websearch_to_tsquery('english', ?)", + "? @@ websearch_to_tsquery(?)", o.fts_content, ^search_query ), diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index bd568d858..06d399b2e 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Application do @@ -14,7 +14,7 @@ defmodule Pleroma.Application do @name Mix.Project.config()[:name] @version Mix.Project.config()[:version] @repository Mix.Project.config()[:source_url] - @env Mix.env() + @mix_env Mix.env() def name, do: @name def version, do: @version @@ -92,20 +92,18 @@ def start(_type, _args) do Pleroma.Web.Plugs.RateLimiter.Supervisor ] ++ cachex_children() ++ - http_children(adapter, @env) ++ + http_children(adapter, @mix_env) ++ [ Pleroma.Stats, Pleroma.JobQueueMonitor, {Majic.Pool, [name: Pleroma.MajicPool, pool_size: Config.get([:majic_pool, :size], 2)]}, - {Oban, Config.get(Oban)} + {Oban, Config.get(Oban)}, + Pleroma.Web.Endpoint ] ++ - task_children(@env) ++ - dont_run_in_test(@env) ++ + task_children(@mix_env) ++ + dont_run_in_test(@mix_env) ++ chat_child(chat_enabled?()) ++ - [ - Pleroma.Web.Endpoint, - Pleroma.Gopher.Server - ] + [Pleroma.Gopher.Server] # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # for other strategies and supported options @@ -145,7 +143,7 @@ def load_custom_modules do raise "Invalid custom modules" {:ok, modules, _warnings} -> - if @env != :test do + if @mix_env != :test do Enum.each(modules, fn mod -> Logger.info("Custom module loaded: #{inspect(mod)}") end) @@ -230,6 +228,12 @@ defp dont_run_in_test(_) do keys: :duplicate, partitions: System.schedulers_online() ]} + ] ++ background_migrators() + end + + defp background_migrators do + [ + Pleroma.Migrators.HashtagsTableMigrator ] end @@ -297,7 +301,16 @@ defp http_children(_, _), do: [] @spec limiters_setup() :: :ok def limiters_setup do - [Pleroma.Web.RichMedia.Helpers, Pleroma.Web.MediaProxy] - |> Enum.each(&ConcurrentLimiter.new(&1, 1, 0)) + config = Config.get(ConcurrentLimiter, []) + + [Pleroma.Web.RichMedia.Helpers, Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy] + |> Enum.each(fn module -> + mod_config = Keyword.get(config, module, []) + + max_running = Keyword.get(mod_config, :max_running, 5) + max_waiting = Keyword.get(mod_config, :max_waiting, 5) + + ConcurrentLimiter.new(module, max_running, max_waiting) + end) end end diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex index e61576644..6ef65b263 100644 --- a/lib/pleroma/application_requirements.ex +++ b/lib/pleroma/application_requirements.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ApplicationRequirements do diff --git a/lib/pleroma/bbs/authenticator.ex b/lib/pleroma/bbs/authenticator.ex index 83ebb756d..241fcb53c 100644 --- a/lib/pleroma/bbs/authenticator.ex +++ b/lib/pleroma/bbs/authenticator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.BBS.Authenticator do diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index cd523cf7d..4a2e255f7 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.BBS.Handler do diff --git a/lib/pleroma/bookmark.ex b/lib/pleroma/bookmark.ex index e6ddbce1b..83cc8e7e1 100644 --- a/lib/pleroma/bookmark.ex +++ b/lib/pleroma/bookmark.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Bookmark do diff --git a/lib/pleroma/caching.ex b/lib/pleroma/caching.ex new file mode 100644 index 000000000..02c18564d --- /dev/null +++ b/lib/pleroma/caching.ex @@ -0,0 +1,19 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Caching do + @callback get!(Cachex.cache(), any()) :: any() + @callback get(Cachex.cache(), any()) :: {atom(), any()} + @callback put(Cachex.cache(), any(), any(), Keyword.t()) :: {Cachex.status(), boolean()} + @callback put(Cachex.cache(), any(), any()) :: {Cachex.status(), boolean()} + @callback fetch!(Cachex.cache(), any(), function() | nil) :: any() + # @callback del(Cachex.cache(), any(), Keyword.t()) :: {Cachex.status(), boolean()} + @callback del(Cachex.cache(), any()) :: {Cachex.status(), boolean()} + @callback stream!(Cachex.cache(), any()) :: Enumerable.t() + @callback expire_at(Cachex.cache(), binary(), number()) :: {Cachex.status(), boolean()} + @callback exists?(Cachex.cache(), any()) :: {Cachex.status(), boolean()} + @callback execute!(Cachex.cache(), function()) :: any() + @callback get_and_update(Cachex.cache(), any(), function()) :: + {:commit | :ignore, any()} +end diff --git a/lib/pleroma/captcha.ex b/lib/pleroma/captcha.ex index 6ab754b6f..bad7b3a66 100644 --- a/lib/pleroma/captcha.ex +++ b/lib/pleroma/captcha.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Captcha do @@ -7,6 +7,8 @@ defmodule Pleroma.Captcha do alias Plug.Crypto.KeyGenerator alias Plug.Crypto.MessageEncryptor + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + @doc """ Ask the configured captcha service for a new captcha """ @@ -86,7 +88,7 @@ defp validate_expiration(created_at) do end defp validate_usage(token) do - if is_nil(Cachex.get!(:used_captcha_cache, token)) do + if is_nil(@cachex.get!(:used_captcha_cache, token)) do :ok else {:error, :already_used} @@ -95,7 +97,7 @@ defp validate_usage(token) do defp mark_captcha_as_used(token) do ttl = seconds_valid() |> :timer.seconds() - Cachex.put(:used_captcha_cache, token, true, ttl: ttl) + @cachex.put(:used_captcha_cache, token, true, ttl: ttl) end defp method, do: Pleroma.Config.get!([__MODULE__, :method]) diff --git a/lib/pleroma/captcha/kocaptcha.ex b/lib/pleroma/captcha/kocaptcha.ex index 201b55ab4..eac6dfa36 100644 --- a/lib/pleroma/captcha/kocaptcha.ex +++ b/lib/pleroma/captcha/kocaptcha.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Captcha.Kocaptcha do diff --git a/lib/pleroma/captcha/native.ex b/lib/pleroma/captcha/native.ex index 8d604d2b2..2c6f64e66 100644 --- a/lib/pleroma/captcha/native.ex +++ b/lib/pleroma/captcha/native.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Captcha.Native do diff --git a/lib/pleroma/captcha/service.ex b/lib/pleroma/captcha/service.ex index 959038cef..a430fafdc 100644 --- a/lib/pleroma/captcha/service.ex +++ b/lib/pleroma/captcha/service.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Captcha.Service do diff --git a/lib/pleroma/chat.ex b/lib/pleroma/chat.ex index 28007cd9f..bacff24b5 100644 --- a/lib/pleroma/chat.ex +++ b/lib/pleroma/chat.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Chat do diff --git a/lib/pleroma/chat/message_reference.ex b/lib/pleroma/chat/message_reference.ex index 131ae0186..89537d155 100644 --- a/lib/pleroma/chat/message_reference.ex +++ b/lib/pleroma/chat/message_reference.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Chat.MessageReference do diff --git a/lib/pleroma/clippy.ex b/lib/pleroma/clippy.ex index ae96e6ad1..9c674e075 100644 --- a/lib/pleroma/clippy.ex +++ b/lib/pleroma/clippy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Clippy do diff --git a/lib/pleroma/config.ex b/lib/pleroma/config.ex index 97f877595..54e332595 100644 --- a/lib/pleroma/config.ex +++ b/lib/pleroma/config.ex @@ -1,16 +1,20 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config do + @behaviour Pleroma.Config.Getting defmodule Error do defexception [:message] end + @impl true def get(key), do: get(key, nil) + @impl true def get([key], default), do: get(key, default) + @impl true def get([_ | _] = path, default) do case fetch(path) do {:ok, value} -> value @@ -18,6 +22,7 @@ def get([_ | _] = path, default) do end end + @impl true def get(key, default) do Application.get_env(:pleroma, key, default) end @@ -95,15 +100,7 @@ def oauth_consumer_strategies, do: get([:auth, :oauth_consumer_strategies], []) def oauth_consumer_enabled?, do: oauth_consumer_strategies() != [] - def enforce_oauth_admin_scope_usage?, do: !!get([:auth, :enforce_oauth_admin_scope_usage]) - - def oauth_admin_scopes(scopes) when is_list(scopes) do - Enum.flat_map( - scopes, - fn scope -> - ["admin:#{scope}"] ++ - if enforce_oauth_admin_scope_usage?(), do: [], else: [scope] - end - ) + def feature_enabled?(feature_name) do + get([:features, feature_name]) not in [nil, false, :disabled, :auto] end end diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index 59c6b0f58..24aa5993b 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.DeprecationWarnings do @@ -40,7 +40,8 @@ def warn do :ok <- check_welcome_message_config(), :ok <- check_gun_pool_options(), :ok <- check_activity_expiration_config(), - :ok <- check_remote_ip_plug_name() do + :ok <- check_remote_ip_plug_name(), + :ok <- check_uploders_s3_public_endpoint() do :ok else _ -> @@ -193,4 +194,25 @@ def check_remote_ip_plug_name do warning_preface ) end + + @spec check_uploders_s3_public_endpoint() :: :ok | nil + def check_uploders_s3_public_endpoint do + s3_config = Pleroma.Config.get([Pleroma.Uploaders.S3]) + + use_old_config = Keyword.has_key?(s3_config, :public_endpoint) + + if use_old_config do + Logger.error(""" + !!!DEPRECATION WARNING!!! + Your config is using the old setting for controlling the URL of media uploaded to your S3 bucket.\n + Please make the following change at your earliest convenience.\n + \n* `config :pleroma, Pleroma.Uploaders.S3, public_endpoint` is now equal to: + \n* `config :pleroma, Pleroma.Upload, base_url` + """) + + :error + else + :ok + end + end end diff --git a/lib/pleroma/config/getting.ex b/lib/pleroma/config/getting.ex new file mode 100644 index 000000000..2cc9fe80b --- /dev/null +++ b/lib/pleroma/config/getting.ex @@ -0,0 +1,8 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Config.Getting do + @callback get(any()) :: any() + @callback get(any(), any()) :: any() +end diff --git a/lib/pleroma/config/helpers.ex b/lib/pleroma/config/helpers.ex index 3dce40ea0..9f26c3546 100644 --- a/lib/pleroma/config/helpers.ex +++ b/lib/pleroma/config/helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.Helpers do diff --git a/lib/pleroma/config/holder.ex b/lib/pleroma/config/holder.ex index a99fc0471..4d186a854 100644 --- a/lib/pleroma/config/holder.ex +++ b/lib/pleroma/config/holder.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.Holder do diff --git a/lib/pleroma/config/loader.ex b/lib/pleroma/config/loader.ex index 64e7de6df..b64d06707 100644 --- a/lib/pleroma/config/loader.ex +++ b/lib/pleroma/config/loader.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.Loader do diff --git a/lib/pleroma/config/oban.ex b/lib/pleroma/config/oban.ex index 8e0351d52..3e63bca40 100644 --- a/lib/pleroma/config/oban.ex +++ b/lib/pleroma/config/oban.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.Oban do diff --git a/lib/pleroma/config/release_runtime_provider.ex b/lib/pleroma/config/release_runtime_provider.ex index 8227195dc..e5e9d3dcd 100644 --- a/lib/pleroma/config/release_runtime_provider.ex +++ b/lib/pleroma/config/release_runtime_provider.ex @@ -1,6 +1,6 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do @moduledoc """ - Imports `runtime.exs` and `{env}.exported_from_db.secret.exs` for elixir releases. + Imports runtime config and `{env}.exported_from_db.secret.exs` for releases. """ @behaviour Config.Provider @@ -8,10 +8,11 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do def init(opts), do: opts @impl true - def load(config, _opts) do + def load(config, opts) do with_defaults = Config.Reader.merge(config, Pleroma.Config.Holder.release_defaults()) - config_path = System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs" + config_path = + opts[:config_path] || System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs" with_runtime_config = if File.exists?(config_path) do @@ -24,7 +25,7 @@ def load(config, _opts) do warning = [ IO.ANSI.red(), IO.ANSI.bright(), - "!!! #{config_path} not found! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file", + "!!! Config path is not declared! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file", IO.ANSI.reset() ] @@ -33,13 +34,14 @@ def load(config, _opts) do end exported_config_path = - config_path - |> Path.dirname() - |> Path.join("prod.exported_from_db.secret.exs") + opts[:exported_config_path] || + config_path + |> Path.dirname() + |> Path.join("#{Pleroma.Config.get(:env)}.exported_from_db.secret.exs") with_exported = if File.exists?(exported_config_path) do - exported_config = Config.Reader.read!(with_runtime_config) + exported_config = Config.Reader.read!(exported_config_path) Config.Reader.merge(with_runtime_config, exported_config) else with_runtime_config diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index a0d7b7d71..aad45aab8 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.TransferTask do diff --git a/lib/pleroma/config_db.ex b/lib/pleroma/config_db.ex index 8e8bb732f..cb57673e3 100644 --- a/lib/pleroma/config_db.ex +++ b/lib/pleroma/config_db.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ConfigDB do @@ -387,6 +387,6 @@ defp find_valid_delimiter([delimiter | others], pattern, regex_delimiter) do @spec module_name?(String.t()) :: boolean() def module_name?(string) do Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Quack|Ueberauth|Swoosh)\./, string) or - string in ["Oban", "Ueberauth", "ExSyslogger"] + string in ["Oban", "Ueberauth", "ExSyslogger", "ConcurrentLimiter"] end end diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex index cf8182d55..b24338cc6 100644 --- a/lib/pleroma/constants.ex +++ b/lib/pleroma/constants.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Constants do @@ -18,7 +18,8 @@ defmodule Pleroma.Constants do "emoji", "context_id", "deleted_activity_id", - "pleroma_internal" + "pleroma_internal", + "generator" ] ) diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index 77933f0be..828e27450 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -1,10 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Conversation do alias Pleroma.Conversation.Participation alias Pleroma.Conversation.Participation.RecipientShip + alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User use Ecto.Schema @@ -58,11 +59,10 @@ def maybe_create_recipientships(participation, activity) do def create_or_bump_for(activity, opts \\ []) do with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), "Create" <- activity.data["type"], - object <- Pleroma.Object.normalize(activity), + %Object{} = object <- Object.normalize(activity, fetch: false), true <- object.data["type"] in ["Note", "Question"], - ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do - {:ok, conversation} = create_for_ap_id(ap_id) - + ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"], + {:ok, conversation} <- create_for_ap_id(ap_id) do users = User.get_users_from_set(activity.recipients, local_only: false) participations = diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index 4c32b273a..e0a3af28b 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Conversation.Participation do @@ -220,4 +220,8 @@ def unread_conversation_count_for_user(user) do select: %{count: count(p.id)} ) end + + def delete(%__MODULE__{} = participation) do + Repo.delete(participation) + end end diff --git a/lib/pleroma/conversation/participation/recipient_ship.ex b/lib/pleroma/conversation/participation/recipient_ship.ex index de40bacac..094c1a176 100644 --- a/lib/pleroma/conversation/participation/recipient_ship.ex +++ b/lib/pleroma/conversation/participation/recipient_ship.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Conversation.Participation.RecipientShip do diff --git a/lib/pleroma/counter_cache.ex b/lib/pleroma/counter_cache.ex index ebd1f603d..1e75d19ae 100644 --- a/lib/pleroma/counter_cache.ex +++ b/lib/pleroma/counter_cache.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.CounterCache do diff --git a/lib/pleroma/data_migration.ex b/lib/pleroma/data_migration.ex new file mode 100644 index 000000000..1377af16e --- /dev/null +++ b/lib/pleroma/data_migration.ex @@ -0,0 +1,45 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.DataMigration do + use Ecto.Schema + + alias Pleroma.DataMigration + alias Pleroma.DataMigration.State + alias Pleroma.Repo + + import Ecto.Changeset + import Ecto.Query + + schema "data_migrations" do + field(:name, :string) + field(:state, State, default: :pending) + field(:feature_lock, :boolean, default: false) + field(:params, :map, default: %{}) + field(:data, :map, default: %{}) + + timestamps() + end + + def changeset(data_migration, params \\ %{}) do + data_migration + |> cast(params, [:name, :state, :feature_lock, :params, :data]) + |> validate_required([:name]) + |> unique_constraint(:name) + end + + def update_one_by_id(id, params \\ %{}) do + with {1, _} <- + from(dm in DataMigration, where: dm.id == ^id) + |> Repo.update_all(set: params) do + :ok + end + end + + def get_by_name(name) do + Repo.get_by(DataMigration, name: name) + end + + def populate_hashtags_table, do: get_by_name("populate_hashtags_table") +end diff --git a/lib/pleroma/delivery.ex b/lib/pleroma/delivery.ex index 0ded2855c..511d5cf58 100644 --- a/lib/pleroma/delivery.ex +++ b/lib/pleroma/delivery.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Delivery do @@ -9,7 +9,6 @@ defmodule Pleroma.Delivery do alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User - alias Pleroma.User import Ecto.Changeset import Ecto.Query diff --git a/lib/pleroma/docs/generator.ex b/lib/pleroma/docs/generator.ex index a70f83b73..e8a68fd41 100644 --- a/lib/pleroma/docs/generator.ex +++ b/lib/pleroma/docs/generator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Docs.Generator do diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex index a583e2a5b..f22432ea4 100644 --- a/lib/pleroma/docs/json.ex +++ b/lib/pleroma/docs/json.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Docs.JSON do diff --git a/lib/pleroma/docs/markdown.ex b/lib/pleroma/docs/markdown.ex index eac0789a6..7e54e9d58 100644 --- a/lib/pleroma/docs/markdown.ex +++ b/lib/pleroma/docs/markdown.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Docs.Markdown do diff --git a/lib/pleroma/ecto_enums.ex b/lib/pleroma/ecto_enums.ex index 6fc47620c..2a9addabc 100644 --- a/lib/pleroma/ecto_enums.ex +++ b/lib/pleroma/ecto_enums.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only import EctoEnum @@ -17,3 +17,11 @@ follow_accept: 2, follow_reject: 3 ) + +defenum(Pleroma.DataMigration.State, + pending: 1, + running: 2, + complete: 3, + failed: 4, + manual: 5 +) diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex index d852c0abd..8552ae73d 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime do diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex index 4aacc5c88..96674e21f 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Emoji do diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex index 8034235b0..45bd6070a 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID do diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex index 205527a96..af4b0e527 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients do diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex index 7f0405c7b..d0f5f381f 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText do diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex index 2054c26be..f5b68648c 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Uri do diff --git a/lib/pleroma/ecto_type/config/atom.ex b/lib/pleroma/ecto_type/config/atom.ex index df565d432..3bf0bca5b 100644 --- a/lib/pleroma/ecto_type/config/atom.ex +++ b/lib/pleroma/ecto_type/config/atom.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.Config.Atom do diff --git a/lib/pleroma/ecto_type/config/binary_value.ex b/lib/pleroma/ecto_type/config/binary_value.ex index bbd2608c5..908220a65 100644 --- a/lib/pleroma/ecto_type/config/binary_value.ex +++ b/lib/pleroma/ecto_type/config/binary_value.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.Config.BinaryValue do diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex index d5757c12a..5fe74e2f7 100644 --- a/lib/pleroma/emails/admin_email.ex +++ b/lib/pleroma/emails/admin_email.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emails.AdminEmail do diff --git a/lib/pleroma/emails/mailer.ex b/lib/pleroma/emails/mailer.ex index 5108c71c8..c68550bee 100644 --- a/lib/pleroma/emails/mailer.ex +++ b/lib/pleroma/emails/mailer.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emails.Mailer do diff --git a/lib/pleroma/emails/new_users_digest_email.ex b/lib/pleroma/emails/new_users_digest_email.ex index 348cbac9c..3552dedae 100644 --- a/lib/pleroma/emails/new_users_digest_email.ex +++ b/lib/pleroma/emails/new_users_digest_email.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emails.NewUsersDigestEmail do diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex index 806a61fd2..52f3d419d 100644 --- a/lib/pleroma/emails/user_email.ex +++ b/lib/pleroma/emails/user_email.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emails.UserEmail do @@ -81,9 +81,9 @@ def account_confirmation_email(user) do ) html_body = """ -

Welcome to #{instance_name()}!

+

Thank you for registering on #{instance_name()}

Email confirmation is required to activate the account.

-

Click the following link to proceed: activate your account.

+

Please click the following link to activate your account.

""" new() @@ -93,6 +93,33 @@ def account_confirmation_email(user) do |> html_body(html_body) end + def approval_pending_email(user) do + html_body = """ +

Awaiting Approval

+

Your account at #{instance_name()} is being reviewed by staff. You will receive another email once your account is approved.

+ """ + + new() + |> to(recipient(user)) + |> from(sender()) + |> subject("Your account is awaiting approval") + |> html_body(html_body) + end + + def successful_registration_email(user) do + html_body = """ +

Hello @#{user.nickname},

+

Your account at #{instance_name()} has been registered successfully.

+

No further action is required to activate your account.

+ """ + + new() + |> to(recipient(user)) + |> from(sender()) + |> subject("Account registered on #{instance_name()}") + |> html_body(html_body) + end + @doc """ Email used in digest email notifications Includes Mentions and New Followers data @@ -106,7 +133,7 @@ def digest_email(user) do notifications |> Enum.filter(&(&1.activity.data["type"] == "Create")) |> Enum.map(fn notification -> - object = Pleroma.Object.normalize(notification.activity) + object = Pleroma.Object.normalize(notification.activity, fetch: false) if not is_nil(object) do object = update_in(object.data["content"], &format_links/1) @@ -129,7 +156,7 @@ def digest_email(user) do if not is_nil(from) do %{ data: notification, - object: Pleroma.Object.normalize(notification.activity), + object: Pleroma.Object.normalize(notification.activity, fetch: false), from: User.get_by_ap_id(notification.activity.actor) } end @@ -151,7 +178,7 @@ def digest_email(user) do logo_path = if is_nil(logo) do - Path.join(:code.priv_dir(:pleroma), "static/static/logo.png") + Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg") else Path.join(Config.get([:instance, :static_dir]), logo) end @@ -162,7 +189,7 @@ def digest_email(user) do |> subject("Your digest from #{instance_name()}") |> put_layout(false) |> render_body("digest.html", html_data) - |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.png", type: :inline)) + |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline)) end end diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex index 513fb59f8..f077fe5b4 100644 --- a/lib/pleroma/emoji.ex +++ b/lib/pleroma/emoji.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emoji do diff --git a/lib/pleroma/emoji/formatter.ex b/lib/pleroma/emoji/formatter.ex index dc45b8a38..50150e951 100644 --- a/lib/pleroma/emoji/formatter.ex +++ b/lib/pleroma/emoji/formatter.ex @@ -1,10 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emoji.Formatter do alias Pleroma.Emoji alias Pleroma.HTML + alias Pleroma.Web alias Pleroma.Web.MediaProxy def emojify(text) do @@ -43,7 +44,7 @@ def get_emoji_map(text) when is_binary(text) do Emoji.get_all() |> Enum.filter(fn {emoji, %Emoji{}} -> String.contains?(text, ":#{emoji}:") end) |> Enum.reduce(%{}, fn {name, %Emoji{file: file}}, acc -> - Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}") + Map.put(acc, name, to_string(URI.merge(Web.base_url(), file))) end) end diff --git a/lib/pleroma/emoji/loader.ex b/lib/pleroma/emoji/loader.ex index 03a6bca0b..67acd7069 100644 --- a/lib/pleroma/emoji/loader.ex +++ b/lib/pleroma/emoji/loader.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emoji.Loader do @@ -15,6 +15,8 @@ defmodule Pleroma.Emoji.Loader do require Logger + @mix_env Mix.env() + @type pattern :: Regex.t() | module() | String.t() @type patterns :: pattern() | [pattern()] @type group_patterns :: keyword(patterns()) @@ -77,10 +79,19 @@ def load do # it should run even if there are no emoji packs shortcode_globs = Config.get([:emoji, :shortcode_globs], []) + # for testing emoji.txt entries we do not want exposed in normal operation + test_emoji = + if @mix_env == :test do + load_from_file("test/config/emoji.txt", emoji_groups) + else + [] + end + emojis_txt = (load_from_file("config/emoji.txt", emoji_groups) ++ load_from_file("config/custom_emoji.txt", emoji_groups) ++ - load_from_globs(shortcode_globs, emoji_groups)) + load_from_globs(shortcode_globs, emoji_groups) ++ + test_emoji) |> Enum.reject(fn value -> value == nil end) Enum.map(emojis ++ emojis_txt, &prepare_emoji/1) diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex index ca58e5432..09bfcc868 100644 --- a/lib/pleroma/emoji/pack.ex +++ b/lib/pleroma/emoji/pack.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emoji.Pack do @@ -20,16 +20,18 @@ defmodule Pleroma.Emoji.Pack do name: String.t() } + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + alias Pleroma.Emoji alias Pleroma.Emoji.Pack + alias Pleroma.Utils @spec create(String.t()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values} def create(name) do with :ok <- validate_not_empty([name]), dir <- Path.join(emoji_path(), name), :ok <- File.mkdir(dir) do - %__MODULE__{pack_file: Path.join(dir, "pack.json")} - |> save_pack() + save_pack(%__MODULE__{pack_file: Path.join(dir, "pack.json")}) end end @@ -62,10 +64,9 @@ def show(opts) do @spec delete(String.t()) :: {:ok, [binary()]} | {:error, File.posix(), binary()} | {:error, :empty_values} def delete(name) do - with :ok <- validate_not_empty([name]) do - emoji_path() - |> Path.join(name) - |> File.rm_rf() + with :ok <- validate_not_empty([name]), + pack_path <- Path.join(emoji_path(), name) do + File.rm_rf(pack_path) end end @@ -94,7 +95,7 @@ defp unpack_zip_emojies(zip_files) do def add_file(%Pack{} = pack, _, _, %Plug.Upload{content_type: "application/zip"} = file) do with {:ok, zip_files} <- :zip.table(to_charlist(file.path)), [_ | _] = emojies <- unpack_zip_emojies(zip_files), - {:ok, tmp_dir} <- Pleroma.Utils.tmp_dir("emoji") do + {:ok, tmp_dir} <- Utils.tmp_dir("emoji") do try do {:ok, _emoji_files} = :zip.unzip( @@ -282,18 +283,21 @@ def update_metadata(name, data) do end end - @spec load_pack(String.t()) :: {:ok, t()} | {:error, :not_found} + @spec load_pack(String.t()) :: {:ok, t()} | {:error, :file.posix()} def load_pack(name) do pack_file = Path.join([emoji_path(), name, "pack.json"]) - if File.exists?(pack_file) do + with {:ok, _} <- File.stat(pack_file), + {:ok, pack_data} <- File.read(pack_file) do pack = - pack_file - |> File.read!() - |> from_json() - |> Map.put(:pack_file, pack_file) - |> Map.put(:path, Path.dirname(pack_file)) - |> Map.put(:name, name) + from_json( + pack_data, + %{ + pack_file: pack_file, + path: Path.dirname(pack_file), + name: name + } + ) files_count = pack.files @@ -301,8 +305,6 @@ def load_pack(name) do |> length() {:ok, Map.put(pack, :files_count, files_count)} - else - {:error, :not_found} end end @@ -415,7 +417,7 @@ defp create_archive_and_cache(pack, hash) do ttl_per_file = Pleroma.Config.get!([:emoji, :shared_pack_cache_seconds_per_file]) overall_ttl = :timer.seconds(ttl_per_file * Enum.count(files)) - Cachex.put!( + @cachex.put( :emoji_packs_cache, pack.name, # if pack.json MD5 changes, the cache is not valid anymore @@ -434,10 +436,17 @@ defp save_pack(pack) do end end - defp from_json(json) do + defp from_json(json, attrs) do map = Jason.decode!(json) - struct(__MODULE__, %{files: map["files"], pack: map["pack"]}) + pack_attrs = + attrs + |> Map.merge(%{ + files: map["files"], + pack: map["pack"] + }) + + struct(__MODULE__, pack_attrs) end defp validate_shareable_packs_available(uri) do @@ -491,10 +500,10 @@ defp rename_file(pack, filename, new_filename) do end defp create_subdirs(file_path) do - if String.contains?(file_path, "/") do - file_path - |> Path.dirname() - |> File.mkdir_p!() + with true <- String.contains?(file_path, "/"), + path <- Path.dirname(file_path), + false <- File.exists?(path) do + File.mkdir_p!(path) end end @@ -518,10 +527,15 @@ defp remove_dir_if_empty(emoji, filename) do defp get_filename(pack, shortcode) do with %{^shortcode => filename} when is_binary(filename) <- pack.files, - true <- pack.path |> Path.join(filename) |> File.exists?() do + file_path <- Path.join(pack.path, filename), + {:ok, _} <- File.stat(file_path) do {:ok, filename} else - _ -> {:error, :doesnt_exist} + {:error, _} = error -> + error + + _ -> + {:error, :doesnt_exist} end end @@ -606,7 +620,7 @@ defp download_archive(url, sha) do defp fetch_archive(pack) do hash = :crypto.hash(:md5, File.read!(pack.pack_file)) - case Cachex.get!(:emoji_packs_cache, pack.name) do + case @cachex.get!(:emoji_packs_cache, pack.name) do %{hash: ^hash, pack_data: archive} -> archive _ -> create_archive_and_cache(pack, hash) end diff --git a/lib/pleroma/filter.ex b/lib/pleroma/filter.ex index 5d6df9530..82b9caf9b 100644 --- a/lib/pleroma/filter.ex +++ b/lib/pleroma/filter.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Filter do @@ -11,6 +11,9 @@ defmodule Pleroma.Filter do alias Pleroma.Repo alias Pleroma.User + @type t() :: %__MODULE__{} + @type format() :: :postgres | :re + schema "filters" do belongs_to(:user, User, type: FlakeId.Ecto.CompatType) field(:filter_id, :integer) @@ -18,15 +21,16 @@ defmodule Pleroma.Filter do field(:whole_word, :boolean, default: true) field(:phrase, :string) field(:context, {:array, :string}) - field(:expires_at, :utc_datetime) + field(:expires_at, :naive_datetime) timestamps() end + @spec get(integer() | String.t(), User.t()) :: t() | nil def get(id, %{id: user_id} = _user) do query = from( - f in Pleroma.Filter, + f in __MODULE__, where: f.filter_id == ^id, where: f.user_id == ^user_id ) @@ -34,14 +38,17 @@ def get(id, %{id: user_id} = _user) do Repo.one(query) end + @spec get_active(Ecto.Query.t() | module()) :: Ecto.Query.t() def get_active(query) do from(f in query, where: is_nil(f.expires_at) or f.expires_at > ^NaiveDateTime.utc_now()) end + @spec get_irreversible(Ecto.Query.t()) :: Ecto.Query.t() def get_irreversible(query) do from(f in query, where: f.hide) end + @spec get_filters(Ecto.Query.t() | module(), User.t()) :: [t()] def get_filters(query \\ __MODULE__, %User{id: user_id}) do query = from( @@ -53,7 +60,32 @@ def get_filters(query \\ __MODULE__, %User{id: user_id}) do Repo.all(query) end - def create(%Pleroma.Filter{user_id: user_id, filter_id: nil} = filter) do + @spec create(map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()} + def create(attrs \\ %{}) do + Repo.transaction(fn -> create_with_expiration(attrs) end) + end + + defp create_with_expiration(attrs) do + with {:ok, filter} <- do_create(attrs), + {:ok, _} <- maybe_add_expiration_job(filter) do + filter + else + {:error, error} -> Repo.rollback(error) + end + end + + defp do_create(attrs) do + %__MODULE__{} + |> cast(attrs, [:phrase, :context, :hide, :expires_at, :whole_word, :user_id, :filter_id]) + |> maybe_add_filter_id() + |> validate_required([:phrase, :context, :user_id, :filter_id]) + |> maybe_add_expires_at(attrs) + |> Repo.insert() + end + + defp maybe_add_filter_id(%{changes: %{filter_id: _}} = changeset), do: changeset + + defp maybe_add_filter_id(%{changes: %{user_id: user_id}} = changeset) do # If filter_id wasn't given, use the max filter_id for this user plus 1. # XXX This could result in a race condition if a user tries to add two # different filters for their account from two different clients at the @@ -61,7 +93,7 @@ def create(%Pleroma.Filter{user_id: user_id, filter_id: nil} = filter) do max_id_query = from( - f in Pleroma.Filter, + f in __MODULE__, where: f.user_id == ^user_id, select: max(f.filter_id) ) @@ -76,34 +108,92 @@ def create(%Pleroma.Filter{user_id: user_id, filter_id: nil} = filter) do max_id + 1 end - filter - |> Map.put(:filter_id, filter_id) - |> Repo.insert() + change(changeset, filter_id: filter_id) end - def create(%Pleroma.Filter{} = filter) do - Repo.insert(filter) + # don't override expires_at, if passed expires_at and expires_in + defp maybe_add_expires_at(%{changes: %{expires_at: %NaiveDateTime{} = _}} = changeset, _) do + changeset end - def delete(%Pleroma.Filter{id: filter_key} = filter) when is_number(filter_key) do - Repo.delete(filter) + defp maybe_add_expires_at(changeset, %{expires_in: expires_in}) + when is_integer(expires_in) and expires_in > 0 do + expires_at = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(expires_in) + |> NaiveDateTime.truncate(:second) + + change(changeset, expires_at: expires_at) end - def delete(%Pleroma.Filter{id: filter_key} = filter) when is_nil(filter_key) do - %Pleroma.Filter{id: id} = get(filter.filter_id, %{id: filter.user_id}) - - filter - |> Map.put(:id, id) - |> Repo.delete() + defp maybe_add_expires_at(changeset, %{expires_in: nil}) do + change(changeset, expires_at: nil) end - def update(%Pleroma.Filter{} = filter, params) do + defp maybe_add_expires_at(changeset, _), do: changeset + + defp maybe_add_expiration_job(%{expires_at: %NaiveDateTime{} = expires_at} = filter) do + Pleroma.Workers.PurgeExpiredFilter.enqueue(%{ + filter_id: filter.id, + expires_at: DateTime.from_naive!(expires_at, "Etc/UTC") + }) + end + + defp maybe_add_expiration_job(_), do: {:ok, nil} + + @spec delete(t()) :: {:ok, t()} | {:error, Ecto.Changeset.t()} + def delete(%__MODULE__{} = filter) do + Repo.transaction(fn -> delete_with_expiration(filter) end) + end + + defp delete_with_expiration(filter) do + with {:ok, _} <- maybe_delete_old_expiration_job(filter, nil), + {:ok, filter} <- Repo.delete(filter) do + filter + else + {:error, error} -> Repo.rollback(error) + end + end + + @spec update(t(), map()) :: {:ok, t()} | {:error, Ecto.Changeset.t()} + def update(%__MODULE__{} = filter, params) do + Repo.transaction(fn -> update_with_expiration(filter, params) end) + end + + defp update_with_expiration(filter, params) do + with {:ok, updated} <- do_update(filter, params), + {:ok, _} <- maybe_delete_old_expiration_job(filter, updated), + {:ok, _} <- + maybe_add_expiration_job(updated) do + updated + else + {:error, error} -> Repo.rollback(error) + end + end + + defp do_update(filter, params) do filter |> cast(params, [:phrase, :context, :hide, :expires_at, :whole_word]) |> validate_required([:phrase, :context]) + |> maybe_add_expires_at(params) |> Repo.update() end + defp maybe_delete_old_expiration_job(%{expires_at: nil}, _), do: {:ok, nil} + + defp maybe_delete_old_expiration_job(%{expires_at: expires_at}, %{expires_at: expires_at}) do + {:ok, nil} + end + + defp maybe_delete_old_expiration_job(%{id: id}, _) do + with %Oban.Job{} = job <- Pleroma.Workers.PurgeExpiredFilter.get_expiration(id) do + Repo.delete(job) + else + nil -> {:ok, nil} + end + end + + @spec compose_regex(User.t() | [t()], format()) :: String.t() | Regex.t() | nil def compose_regex(user_or_filters, format \\ :postgres) def compose_regex(%User{} = user, format) do diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index 5390a58e1..a0c7e6e39 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.FollowingRelationship do @@ -152,7 +152,7 @@ def get_follow_requests(%User{id: id}) do |> join(:inner, [r], f in assoc(r, :follower)) |> where([r], r.state == ^:follow_pending) |> where([r], r.following_id == ^id) - |> where([r, f], f.deactivated != true) + |> where([r, f], f.is_active == true) |> select([r, f], f) |> Repo.all() end diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index baf652a5a..764e347ec 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Formatter do diff --git a/lib/pleroma/frontend.ex b/lib/pleroma/frontend.ex index bf935a728..34b7befb8 100644 --- a/lib/pleroma/frontend.ex +++ b/lib/pleroma/frontend.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Frontend do diff --git a/lib/pleroma/gopher/server.ex b/lib/pleroma/gopher/server.ex index e9f54c4c0..1b85c49f5 100644 --- a/lib/pleroma/gopher/server.ex +++ b/lib/pleroma/gopher/server.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gopher.Server do @@ -76,7 +76,7 @@ def render_activities(activities) do |> Enum.map(fn activity -> user = User.get_cached_by_ap_id(activity.data["actor"]) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) like_count = object.data["like_count"] || 0 announcement_count = object.data["announcement_count"] || 0 diff --git a/lib/pleroma/gun.ex b/lib/pleroma/gun.ex index 4043e4880..f9c828fac 100644 --- a/lib/pleroma/gun.ex +++ b/lib/pleroma/gun.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun do diff --git a/lib/pleroma/gun/api.ex b/lib/pleroma/gun/api.ex index 09be74392..24d542781 100644 --- a/lib/pleroma/gun/api.ex +++ b/lib/pleroma/gun/api.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.API do diff --git a/lib/pleroma/gun/conn.ex b/lib/pleroma/gun/conn.ex index 477e19c6e..a56625699 100644 --- a/lib/pleroma/gun/conn.ex +++ b/lib/pleroma/gun/conn.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.Conn do diff --git a/lib/pleroma/gun/connection_pool.ex b/lib/pleroma/gun/connection_pool.ex index e322f192a..f9fd77ade 100644 --- a/lib/pleroma/gun/connection_pool.ex +++ b/lib/pleroma/gun/connection_pool.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.ConnectionPool do diff --git a/lib/pleroma/gun/connection_pool/reclaimer.ex b/lib/pleroma/gun/connection_pool/reclaimer.ex index 241e8b04f..c37b62bf2 100644 --- a/lib/pleroma/gun/connection_pool/reclaimer.ex +++ b/lib/pleroma/gun/connection_pool/reclaimer.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.ConnectionPool.Reclaimer do diff --git a/lib/pleroma/gun/connection_pool/worker.ex b/lib/pleroma/gun/connection_pool/worker.ex index b71816bed..02bfff274 100644 --- a/lib/pleroma/gun/connection_pool/worker.ex +++ b/lib/pleroma/gun/connection_pool/worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.ConnectionPool.Worker do diff --git a/lib/pleroma/gun/connection_pool/worker_supervisor.ex b/lib/pleroma/gun/connection_pool/worker_supervisor.ex index 4c23bcbd9..016b675f4 100644 --- a/lib/pleroma/gun/connection_pool/worker_supervisor.ex +++ b/lib/pleroma/gun/connection_pool/worker_supervisor.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do diff --git a/lib/pleroma/hashtag.ex b/lib/pleroma/hashtag.ex new file mode 100644 index 000000000..53e2e9c89 --- /dev/null +++ b/lib/pleroma/hashtag.ex @@ -0,0 +1,106 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Hashtag do + use Ecto.Schema + + import Ecto.Changeset + import Ecto.Query + + alias Ecto.Multi + alias Pleroma.Hashtag + alias Pleroma.Object + alias Pleroma.Repo + + schema "hashtags" do + field(:name, :string) + + many_to_many(:objects, Object, join_through: "hashtags_objects", on_replace: :delete) + + timestamps() + end + + def normalize_name(name) do + name + |> String.downcase() + |> String.trim() + end + + def get_or_create_by_name(name) do + changeset = changeset(%Hashtag{}, %{name: name}) + + Repo.insert( + changeset, + on_conflict: [set: [name: get_field(changeset, :name)]], + conflict_target: :name, + returning: true + ) + end + + def get_or_create_by_names(names) when is_list(names) do + names = Enum.map(names, &normalize_name/1) + timestamp = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second) + + structs = + Enum.map(names, fn name -> + %Hashtag{} + |> changeset(%{name: name}) + |> Map.get(:changes) + |> Map.merge(%{inserted_at: timestamp, updated_at: timestamp}) + end) + + try do + with {:ok, %{query_op: hashtags}} <- + Multi.new() + |> Multi.insert_all(:insert_all_op, Hashtag, structs, + on_conflict: :nothing, + conflict_target: :name + ) + |> Multi.run(:query_op, fn _repo, _changes -> + {:ok, Repo.all(from(ht in Hashtag, where: ht.name in ^names))} + end) + |> Repo.transaction() do + {:ok, hashtags} + else + {:error, _name, value, _changes_so_far} -> {:error, value} + end + rescue + e -> {:error, e} + end + end + + def changeset(%Hashtag{} = struct, params) do + struct + |> cast(params, [:name]) + |> update_change(:name, &normalize_name/1) + |> validate_required([:name]) + |> unique_constraint(:name) + end + + def unlink(%Object{id: object_id}) do + with {_, hashtag_ids} <- + from(hto in "hashtags_objects", + where: hto.object_id == ^object_id, + select: hto.hashtag_id + ) + |> Repo.delete_all(), + {:ok, unreferenced_count} <- delete_unreferenced(hashtag_ids) do + {:ok, length(hashtag_ids), unreferenced_count} + end + end + + @delete_unreferenced_query """ + DELETE FROM hashtags WHERE id IN + (SELECT hashtags.id FROM hashtags + LEFT OUTER JOIN hashtags_objects + ON hashtags_objects.hashtag_id = hashtags.id + WHERE hashtags_objects.hashtag_id IS NULL AND hashtags.id = ANY($1)); + """ + + def delete_unreferenced(ids) do + with {:ok, %{num_rows: deleted_count}} <- Repo.query(@delete_unreferenced_query, [ids]) do + {:ok, deleted_count} + end + end +end diff --git a/lib/pleroma/healthcheck.ex b/lib/pleroma/healthcheck.ex index 92ce83cb7..c905bba3f 100644 --- a/lib/pleroma/healthcheck.ex +++ b/lib/pleroma/healthcheck.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Healthcheck do diff --git a/lib/pleroma/helpers/auth_helper.ex b/lib/pleroma/helpers/auth_helper.ex index 8f87b38be..13e4c8158 100644 --- a/lib/pleroma/helpers/auth_helper.ex +++ b/lib/pleroma/helpers/auth_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Helpers.AuthHelper do diff --git a/lib/pleroma/helpers/inet_helper.ex b/lib/pleroma/helpers/inet_helper.ex index 126f82381..5acdfaed0 100644 --- a/lib/pleroma/helpers/inet_helper.ex +++ b/lib/pleroma/helpers/inet_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Helpers.InetHelper do diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex index 6b799173e..738adfcaa 100644 --- a/lib/pleroma/helpers/media_helper.ex +++ b/lib/pleroma/helpers/media_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Helpers.MediaHelper do diff --git a/lib/pleroma/helpers/qt_fast_start.ex b/lib/pleroma/helpers/qt_fast_start.ex index bb93224b5..c4d11b9dd 100644 --- a/lib/pleroma/helpers/qt_fast_start.ex +++ b/lib/pleroma/helpers/qt_fast_start.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Helpers.QtFastStart do diff --git a/lib/pleroma/helpers/uri_helper.ex b/lib/pleroma/helpers/uri_helper.ex index f1301f055..8f6a664ad 100644 --- a/lib/pleroma/helpers/uri_helper.ex +++ b/lib/pleroma/helpers/uri_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Helpers.UriHelper do diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index 43e9145be..2dfdca693 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -1,11 +1,13 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTML do # Scrubbers are compiled on boot so they can be configured in OTP releases # @on_load :compile_scrubbers + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + def compile_scrubbers do dir = Path.join(:code.priv_dir(:pleroma), "scrubbers") @@ -56,8 +58,8 @@ def get_cached_scrubbed_html_for_activity( ) do key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}" - Cachex.fetch!(:scrubber_cache, key, fn _key -> - object = Pleroma.Object.normalize(activity) + @cachex.fetch!(:scrubber_cache, key, fn _key -> + object = Pleroma.Object.normalize(activity, fetch: false) ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false, callback) end) end @@ -105,7 +107,7 @@ def extract_first_external_url_from_object(%{data: %{"content" => content}} = ob unless object.data["fake"] do key = "URL|#{object.id}" - Cachex.fetch!(:scrubber_cache, key, fn _key -> + @cachex.fetch!(:scrubber_cache, key, fn _key -> {:commit, {:ok, extract_first_external_url(content)}} end) else diff --git a/lib/pleroma/http.ex b/lib/pleroma/http.ex index 052597191..07b3ab0ae 100644 --- a/lib/pleroma/http.ex +++ b/lib/pleroma/http.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP do diff --git a/lib/pleroma/http/adapter_helper.ex b/lib/pleroma/http/adapter_helper.ex index 08b51578a..c667afd25 100644 --- a/lib/pleroma/http/adapter_helper.ex +++ b/lib/pleroma/http/adapter_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.AdapterHelper do diff --git a/lib/pleroma/http/adapter_helper/default.ex b/lib/pleroma/http/adapter_helper/default.ex index 8567a616b..a1614b9c5 100644 --- a/lib/pleroma/http/adapter_helper/default.ex +++ b/lib/pleroma/http/adapter_helper/default.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.AdapterHelper.Default do diff --git a/lib/pleroma/http/adapter_helper/gun.ex b/lib/pleroma/http/adapter_helper/gun.ex index 1dbb71362..82c7fd654 100644 --- a/lib/pleroma/http/adapter_helper/gun.ex +++ b/lib/pleroma/http/adapter_helper/gun.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.AdapterHelper.Gun do diff --git a/lib/pleroma/http/adapter_helper/hackney.ex b/lib/pleroma/http/adapter_helper/hackney.ex index ff60513fd..fe3f91a72 100644 --- a/lib/pleroma/http/adapter_helper/hackney.ex +++ b/lib/pleroma/http/adapter_helper/hackney.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.AdapterHelper.Hackney do diff --git a/lib/pleroma/http/ex_aws.ex b/lib/pleroma/http/ex_aws.ex index 5cac3532f..283590b18 100644 --- a/lib/pleroma/http/ex_aws.ex +++ b/lib/pleroma/http/ex_aws.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.ExAws do diff --git a/lib/pleroma/http/request.ex b/lib/pleroma/http/request.ex index 761bd6ccf..d906024de 100644 --- a/lib/pleroma/http/request.ex +++ b/lib/pleroma/http/request.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.Request do diff --git a/lib/pleroma/http/request_builder.ex b/lib/pleroma/http/request_builder.ex index 8a44a001d..631c927af 100644 --- a/lib/pleroma/http/request_builder.ex +++ b/lib/pleroma/http/request_builder.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.RequestBuilder do diff --git a/lib/pleroma/http/tzdata.ex b/lib/pleroma/http/tzdata.ex index 09cfdadf7..77e1b537e 100644 --- a/lib/pleroma/http/tzdata.ex +++ b/lib/pleroma/http/tzdata.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.Tzdata do diff --git a/lib/pleroma/http/web_push.ex b/lib/pleroma/http/web_push.ex index 78148a12e..51f72fbf8 100644 --- a/lib/pleroma/http/web_push.ex +++ b/lib/pleroma/http/web_push.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.WebPush do diff --git a/lib/pleroma/instances.ex b/lib/pleroma/instances.ex index 7315bd7cb..80addcc52 100644 --- a/lib/pleroma/instances.ex +++ b/lib/pleroma/instances.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Instances do diff --git a/lib/pleroma/instances/instance.ex b/lib/pleroma/instances/instance.ex index 2e1696fe2..4d0e8034d 100644 --- a/lib/pleroma/instances/instance.ex +++ b/lib/pleroma/instances/instance.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Instances.Instance do diff --git a/lib/pleroma/job_queue_monitor.ex b/lib/pleroma/job_queue_monitor.ex index c255a61ec..b5f124923 100644 --- a/lib/pleroma/job_queue_monitor.ex +++ b/lib/pleroma/job_queue_monitor.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.JobQueueMonitor do diff --git a/lib/pleroma/jwt.ex b/lib/pleroma/jwt.ex index faeb77781..c75c44bd1 100644 --- a/lib/pleroma/jwt.ex +++ b/lib/pleroma/jwt.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.JWT do diff --git a/lib/pleroma/keys.ex b/lib/pleroma/keys.ex index c9af79f00..413861b15 100644 --- a/lib/pleroma/keys.ex +++ b/lib/pleroma/keys.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Keys do diff --git a/lib/pleroma/list.ex b/lib/pleroma/list.ex index 89aa7b5d4..fe5721c34 100644 --- a/lib/pleroma/list.ex +++ b/lib/pleroma/list.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.List do @@ -113,11 +113,15 @@ def create(title, %User{} = creator) do end end - def follow(%Pleroma.List{following: following} = list, %User{} = followed) do + def follow(%Pleroma.List{id: id}, %User{} = followed) do + list = Repo.get(Pleroma.List, id) + %{following: following} = list update_follows(list, %{following: Enum.uniq([followed.follower_address | following])}) end - def unfollow(%Pleroma.List{following: following} = list, %User{} = unfollowed) do + def unfollow(%Pleroma.List{id: id}, %User{} = unfollowed) do + list = Repo.get(Pleroma.List, id) + %{following: following} = list update_follows(list, %{following: List.delete(following, unfollowed.follower_address)}) end diff --git a/lib/pleroma/logging.ex b/lib/pleroma/logging.ex new file mode 100644 index 000000000..11e1c3bed --- /dev/null +++ b/lib/pleroma/logging.ex @@ -0,0 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Logging do + @callback error(String.t()) :: any() +end diff --git a/lib/pleroma/maintenance.ex b/lib/pleroma/maintenance.ex index 326c17825..f1058b68a 100644 --- a/lib/pleroma/maintenance.ex +++ b/lib/pleroma/maintenance.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Maintenance do diff --git a/lib/pleroma/maps.ex b/lib/pleroma/maps.ex index ab2e32e2f..0d2e94248 100644 --- a/lib/pleroma/maps.ex +++ b/lib/pleroma/maps.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Maps do diff --git a/lib/pleroma/marker.ex b/lib/pleroma/marker.ex index 4d82860f5..9909de161 100644 --- a/lib/pleroma/marker.ex +++ b/lib/pleroma/marker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Marker do diff --git a/lib/pleroma/mfa.ex b/lib/pleroma/mfa.ex index 01b743f4f..02dce7d49 100644 --- a/lib/pleroma/mfa.ex +++ b/lib/pleroma/mfa.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA do @@ -71,7 +71,7 @@ def invalidate_backup_code(%User{} = user, hash_code) do @spec generate_backup_codes(User.t()) :: {:ok, list(binary)} | {:error, String.t()} def generate_backup_codes(%User{} = user) do with codes <- BackupCodes.generate(), - hashed_codes <- Enum.map(codes, &Pbkdf2.hash_pwd_salt/1), + hashed_codes <- Enum.map(codes, &Pleroma.Password.Pbkdf2.hash_pwd_salt/1), changeset <- Changeset.cast_backup_codes(user, hashed_codes), {:ok, _} <- User.update_and_set_cache(changeset) do {:ok, codes} diff --git a/lib/pleroma/mfa/backup_codes.ex b/lib/pleroma/mfa/backup_codes.ex index 9875310ff..a7a1fba2e 100644 --- a/lib/pleroma/mfa/backup_codes.ex +++ b/lib/pleroma/mfa/backup_codes.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.BackupCodes do diff --git a/lib/pleroma/mfa/changeset.ex b/lib/pleroma/mfa/changeset.ex index 77c4fa202..2d46cdf73 100644 --- a/lib/pleroma/mfa/changeset.ex +++ b/lib/pleroma/mfa/changeset.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.Changeset do diff --git a/lib/pleroma/mfa/settings.ex b/lib/pleroma/mfa/settings.ex index de6e2228f..94fbff635 100644 --- a/lib/pleroma/mfa/settings.ex +++ b/lib/pleroma/mfa/settings.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.Settings do diff --git a/lib/pleroma/mfa/token.ex b/lib/pleroma/mfa/token.ex index 69b64c0e8..76573182a 100644 --- a/lib/pleroma/mfa/token.ex +++ b/lib/pleroma/mfa/token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.Token do diff --git a/lib/pleroma/mfa/totp.ex b/lib/pleroma/mfa/totp.ex index d2ea2b3aa..f33e3a379 100644 --- a/lib/pleroma/mfa/totp.ex +++ b/lib/pleroma/mfa/totp.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.TOTP do diff --git a/lib/pleroma/migration_helper/notification_backfill.ex b/lib/pleroma/migration_helper/notification_backfill.ex index 24f4733fe..62b710f82 100644 --- a/lib/pleroma/migration_helper/notification_backfill.ex +++ b/lib/pleroma/migration_helper/notification_backfill.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MigrationHelper.NotificationBackfill do diff --git a/lib/pleroma/migrators/hashtags_table_migrator.ex b/lib/pleroma/migrators/hashtags_table_migrator.ex new file mode 100644 index 000000000..b84058e11 --- /dev/null +++ b/lib/pleroma/migrators/hashtags_table_migrator.ex @@ -0,0 +1,208 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Migrators.HashtagsTableMigrator do + defmodule State do + use Pleroma.Migrators.Support.BaseMigratorState + + @impl Pleroma.Migrators.Support.BaseMigratorState + defdelegate data_migration(), to: Pleroma.DataMigration, as: :populate_hashtags_table + end + + use Pleroma.Migrators.Support.BaseMigrator + + alias Pleroma.Hashtag + alias Pleroma.Migrators.Support.BaseMigrator + alias Pleroma.Object + + @impl BaseMigrator + def feature_config_path, do: [:features, :improved_hashtag_timeline] + + @impl BaseMigrator + def fault_rate_allowance, do: Config.get([:populate_hashtags_table, :fault_rate_allowance], 0) + + @impl BaseMigrator + def perform do + data_migration_id = data_migration_id() + max_processed_id = get_stat(:max_processed_id, 0) + + Logger.info("Transferring embedded hashtags to `hashtags` (from oid: #{max_processed_id})...") + + query() + |> where([object], object.id > ^max_processed_id) + |> Repo.chunk_stream(100, :batches, timeout: :infinity) + |> Stream.each(fn objects -> + object_ids = Enum.map(objects, & &1.id) + + results = Enum.map(objects, &transfer_object_hashtags(&1)) + + failed_ids = + results + |> Enum.filter(&(elem(&1, 0) == :error)) + |> Enum.map(&elem(&1, 1)) + + # Count of objects with hashtags: `{:noop, id}` is returned for objects having other AS2 tags + chunk_affected_count = + results + |> Enum.filter(&(elem(&1, 0) == :ok)) + |> length() + + for failed_id <- failed_ids do + _ = + Repo.query( + "INSERT INTO data_migration_failed_ids(data_migration_id, record_id) " <> + "VALUES ($1, $2) ON CONFLICT DO NOTHING;", + [data_migration_id, failed_id] + ) + end + + _ = + Repo.query( + "DELETE FROM data_migration_failed_ids " <> + "WHERE data_migration_id = $1 AND record_id = ANY($2)", + [data_migration_id, object_ids -- failed_ids] + ) + + max_object_id = Enum.at(object_ids, -1) + + put_stat(:max_processed_id, max_object_id) + increment_stat(:iteration_processed_count, length(object_ids)) + increment_stat(:processed_count, length(object_ids)) + increment_stat(:failed_count, length(failed_ids)) + increment_stat(:affected_count, chunk_affected_count) + put_stat(:records_per_second, records_per_second()) + persist_state() + + # A quick and dirty approach to controlling the load this background migration imposes + sleep_interval = Config.get([:populate_hashtags_table, :sleep_interval_ms], 0) + Process.sleep(sleep_interval) + end) + |> Stream.run() + end + + @impl BaseMigrator + def query do + # Note: most objects have Mention-type AS2 tags and no hashtags (but we can't filter them out) + # Note: not checking activity type, expecting remove_non_create_objects_hashtags/_ to clean up + from( + object in Object, + where: + fragment("(?)->'tag' IS NOT NULL AND (?)->'tag' != '[]'::jsonb", object.data, object.data), + select: %{ + id: object.id, + tag: fragment("(?)->'tag'", object.data) + } + ) + |> join(:left, [o], hashtags_objects in fragment("SELECT object_id FROM hashtags_objects"), + on: hashtags_objects.object_id == o.id + ) + |> where([_o, hashtags_objects], is_nil(hashtags_objects.object_id)) + end + + @spec transfer_object_hashtags(Map.t()) :: {:noop | :ok | :error, integer()} + defp transfer_object_hashtags(object) do + embedded_tags = if Map.has_key?(object, :tag), do: object.tag, else: object.data["tag"] + hashtags = Object.object_data_hashtags(%{"tag" => embedded_tags}) + + if Enum.any?(hashtags) do + transfer_object_hashtags(object, hashtags) + else + {:noop, object.id} + end + end + + defp transfer_object_hashtags(object, hashtags) do + Repo.transaction(fn -> + with {:ok, hashtag_records} <- Hashtag.get_or_create_by_names(hashtags) do + maps = Enum.map(hashtag_records, &%{hashtag_id: &1.id, object_id: object.id}) + base_error = "ERROR when inserting hashtags_objects for object with id #{object.id}" + + try do + with {rows_count, _} when is_integer(rows_count) <- + Repo.insert_all("hashtags_objects", maps, on_conflict: :nothing) do + object.id + else + e -> + Logger.error("#{base_error}: #{inspect(e)}") + Repo.rollback(object.id) + end + rescue + e -> + Logger.error("#{base_error}: #{inspect(e)}") + Repo.rollback(object.id) + end + else + e -> + error = "ERROR: could not create hashtags for object #{object.id}: #{inspect(e)}" + Logger.error(error) + Repo.rollback(object.id) + end + end) + end + + @impl BaseMigrator + def retry_failed do + data_migration_id = data_migration_id() + + failed_objects_query() + |> Repo.chunk_stream(100, :one) + |> Stream.each(fn object -> + with {res, _} when res != :error <- transfer_object_hashtags(object) do + _ = + Repo.query( + "DELETE FROM data_migration_failed_ids " <> + "WHERE data_migration_id = $1 AND record_id = $2", + [data_migration_id, object.id] + ) + end + end) + |> Stream.run() + + put_stat(:failed_count, failures_count()) + persist_state() + + force_continue() + end + + defp failed_objects_query do + from(o in Object) + |> join(:inner, [o], dmf in fragment("SELECT * FROM data_migration_failed_ids"), + on: dmf.record_id == o.id + ) + |> where([_o, dmf], dmf.data_migration_id == ^data_migration_id()) + |> order_by([o], asc: o.id) + end + + @doc """ + Service func to delete `hashtags_objects` for legacy objects not associated with Create activity. + Also deletes unreferenced `hashtags` records (might occur after deletion of `hashtags_objects`). + """ + def delete_non_create_activities_hashtags do + hashtags_objects_cleanup_query = """ + DELETE FROM hashtags_objects WHERE object_id IN + (SELECT DISTINCT objects.id FROM objects + JOIN hashtags_objects ON hashtags_objects.object_id = objects.id LEFT JOIN activities + ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = + (objects.data->>'id') + AND activities.data->>'type' = 'Create' + WHERE activities.id IS NULL); + """ + + hashtags_cleanup_query = """ + DELETE FROM hashtags WHERE id IN + (SELECT hashtags.id FROM hashtags + LEFT OUTER JOIN hashtags_objects + ON hashtags_objects.hashtag_id = hashtags.id + WHERE hashtags_objects.hashtag_id IS NULL); + """ + + {:ok, %{num_rows: hashtags_objects_count}} = + Repo.query(hashtags_objects_cleanup_query, [], timeout: :infinity) + + {:ok, %{num_rows: hashtags_count}} = + Repo.query(hashtags_cleanup_query, [], timeout: :infinity) + + {:ok, hashtags_objects_count, hashtags_count} + end +end diff --git a/lib/pleroma/migrators/support/base_migrator.ex b/lib/pleroma/migrators/support/base_migrator.ex new file mode 100644 index 000000000..1f8a5402b --- /dev/null +++ b/lib/pleroma/migrators/support/base_migrator.ex @@ -0,0 +1,210 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Migrators.Support.BaseMigrator do + @moduledoc """ + Base background migrator functionality. + """ + + @callback perform() :: any() + @callback retry_failed() :: any() + @callback feature_config_path() :: list(atom()) + @callback query() :: Ecto.Query.t() + @callback fault_rate_allowance() :: integer() | float() + + defmacro __using__(_opts) do + quote do + use GenServer + + require Logger + + import Ecto.Query + + alias __MODULE__.State + alias Pleroma.Config + alias Pleroma.Repo + + @behaviour Pleroma.Migrators.Support.BaseMigrator + + defdelegate data_migration(), to: State + defdelegate data_migration_id(), to: State + defdelegate state(), to: State + defdelegate persist_state(), to: State, as: :persist_to_db + defdelegate get_stat(key, value \\ nil), to: State, as: :get_data_key + defdelegate put_stat(key, value), to: State, as: :put_data_key + defdelegate increment_stat(key, increment), to: State, as: :increment_data_key + + @reg_name {:global, __MODULE__} + + def whereis, do: GenServer.whereis(@reg_name) + + def start_link(_) do + case whereis() do + nil -> + GenServer.start_link(__MODULE__, nil, name: @reg_name) + + pid -> + {:ok, pid} + end + end + + @impl true + def init(_) do + {:ok, nil, {:continue, :init_state}} + end + + @impl true + def handle_continue(:init_state, _state) do + {:ok, _} = State.start_link(nil) + + data_migration = data_migration() + manual_migrations = Config.get([:instance, :manual_data_migrations], []) + + cond do + Config.get(:env) == :test -> + update_status(:noop) + + is_nil(data_migration) -> + message = "Data migration does not exist." + update_status(:failed, message) + Logger.error("#{__MODULE__}: #{message}") + + data_migration.state == :manual or data_migration.name in manual_migrations -> + message = "Data migration is in manual execution or manual fix mode." + update_status(:manual, message) + Logger.warn("#{__MODULE__}: #{message}") + + data_migration.state == :complete -> + on_complete(data_migration) + + true -> + send(self(), :perform) + end + + {:noreply, nil} + end + + @impl true + def handle_info(:perform, state) do + State.reinit() + + update_status(:running) + put_stat(:iteration_processed_count, 0) + put_stat(:started_at, NaiveDateTime.utc_now()) + + perform() + + fault_rate = fault_rate() + put_stat(:fault_rate, fault_rate) + fault_rate_allowance = fault_rate_allowance() + + cond do + fault_rate == 0 -> + set_complete() + + is_float(fault_rate) and fault_rate <= fault_rate_allowance -> + message = """ + Done with fault rate of #{fault_rate} which doesn't exceed #{fault_rate_allowance}. + Putting data migration to manual fix mode. Try running `#{__MODULE__}.retry_failed/0`. + """ + + Logger.warn("#{__MODULE__}: #{message}") + update_status(:manual, message) + on_complete(data_migration()) + + true -> + message = "Too many failures. Try running `#{__MODULE__}.retry_failed/0`." + Logger.error("#{__MODULE__}: #{message}") + update_status(:failed, message) + end + + persist_state() + {:noreply, state} + end + + defp on_complete(data_migration) do + if data_migration.feature_lock || feature_state() == :disabled do + Logger.warn( + "#{__MODULE__}: migration complete but feature is locked; consider enabling." + ) + + :noop + else + Config.put(feature_config_path(), :enabled) + :ok + end + end + + @doc "Approximate count for current iteration (including processed records count)" + def count(force \\ false, timeout \\ :infinity) do + stored_count = get_stat(:count) + + if stored_count && !force do + stored_count + else + processed_count = get_stat(:processed_count, 0) + max_processed_id = get_stat(:max_processed_id, 0) + query = where(query(), [entity], entity.id > ^max_processed_id) + + count = Repo.aggregate(query, :count, :id, timeout: timeout) + processed_count + put_stat(:count, count) + persist_state() + + count + end + end + + def failures_count do + with {:ok, %{rows: [[count]]}} <- + Repo.query( + "SELECT COUNT(record_id) FROM data_migration_failed_ids WHERE data_migration_id = $1;", + [data_migration_id()] + ) do + count + end + end + + def feature_state, do: Config.get(feature_config_path()) + + def force_continue do + send(whereis(), :perform) + end + + def force_restart do + :ok = State.reset() + force_continue() + end + + def set_complete do + update_status(:complete) + persist_state() + on_complete(data_migration()) + end + + defp update_status(status, message \\ nil) do + put_stat(:state, status) + put_stat(:message, message) + end + + defp fault_rate do + with failures_count when is_integer(failures_count) <- failures_count() do + failures_count / Enum.max([get_stat(:affected_count, 0), 1]) + else + _ -> :error + end + end + + defp records_per_second do + get_stat(:iteration_processed_count, 0) / Enum.max([running_time(), 1]) + end + + defp running_time do + NaiveDateTime.diff( + NaiveDateTime.utc_now(), + get_stat(:started_at, NaiveDateTime.utc_now()) + ) + end + end + end +end diff --git a/lib/pleroma/migrators/support/base_migrator_state.ex b/lib/pleroma/migrators/support/base_migrator_state.ex new file mode 100644 index 000000000..b698587f2 --- /dev/null +++ b/lib/pleroma/migrators/support/base_migrator_state.ex @@ -0,0 +1,117 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Migrators.Support.BaseMigratorState do + @moduledoc """ + Base background migrator state functionality. + """ + + @callback data_migration() :: Pleroma.DataMigration.t() + + defmacro __using__(_opts) do + quote do + use Agent + + alias Pleroma.DataMigration + + @behaviour Pleroma.Migrators.Support.BaseMigratorState + @reg_name {:global, __MODULE__} + + def start_link(_) do + Agent.start_link(fn -> load_state_from_db() end, name: @reg_name) + end + + def data_migration, do: raise("data_migration/0 is not implemented") + defoverridable data_migration: 0 + + defp load_state_from_db do + data_migration = data_migration() + + data = + if data_migration do + Map.new(data_migration.data, fn {k, v} -> {String.to_atom(k), v} end) + else + %{} + end + + %{ + data_migration_id: data_migration && data_migration.id, + data: data + } + end + + def persist_to_db do + %{data_migration_id: data_migration_id, data: data} = state() + + if data_migration_id do + DataMigration.update_one_by_id(data_migration_id, data: data) + else + {:error, :nil_data_migration_id} + end + end + + def reset do + %{data_migration_id: data_migration_id} = state() + + with false <- is_nil(data_migration_id), + :ok <- + DataMigration.update_one_by_id(data_migration_id, + state: :pending, + data: %{} + ) do + reinit() + else + true -> {:error, :nil_data_migration_id} + e -> e + end + end + + def reinit do + Agent.update(@reg_name, fn _state -> load_state_from_db() end) + end + + def state do + Agent.get(@reg_name, & &1) + end + + def get_data_key(key, default \\ nil) do + get_in(state(), [:data, key]) || default + end + + def put_data_key(key, value) do + _ = persist_non_data_change(key, value) + + Agent.update(@reg_name, fn state -> + put_in(state, [:data, key], value) + end) + end + + def increment_data_key(key, increment \\ 1) do + Agent.update(@reg_name, fn state -> + initial_value = get_in(state, [:data, key]) || 0 + updated_value = initial_value + increment + put_in(state, [:data, key], updated_value) + end) + end + + defp persist_non_data_change(:state, value) do + with true <- get_data_key(:state) != value, + true <- value in Pleroma.DataMigration.State.__valid_values__(), + %{data_migration_id: data_migration_id} when not is_nil(data_migration_id) <- + state() do + DataMigration.update_one_by_id(data_migration_id, state: value) + else + false -> :ok + _ -> {:error, :nil_data_migration_id} + end + end + + defp persist_non_data_change(_, _) do + nil + end + + def data_migration_id, do: Map.get(state(), :data_migration_id) + end + end +end diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index a7f26793d..1849cacc8 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ModerationLog do diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index dd7a1c824..7efbdc49a 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Notification do @@ -112,13 +112,6 @@ def for_user_query(user, opts \\ %{}) do Notification |> where(user_id: ^user.id) - |> where( - [n, a], - fragment( - "? not in (SELECT ap_id FROM users WHERE deactivated = 'true')", - a.actor - ) - ) |> join(:inner, [n], activity in assoc(n, :activity)) |> join(:left, [n, a], object in Object, on: @@ -129,7 +122,9 @@ def for_user_query(user, opts \\ %{}) do a.data ) ) + |> join(:inner, [_n, a], u in User, on: u.ap_id == a.actor, as: :user_actor) |> preload([n, a, o], activity: {a, object: o}) + |> where([user_actor: user_actor], user_actor.is_active) |> exclude_notification_muted(user, exclude_notification_muted_opts) |> exclude_blocked(user, exclude_blocked_opts) |> exclude_filtered(user) @@ -156,9 +151,10 @@ defp exclude_notification_muted(query, user, opts) do query |> where([n, a], a.actor not in ^notification_muted_ap_ids) |> join(:left, [n, a], tm in ThreadMute, - on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data) + on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data), + as: :thread_mute ) - |> where([n, a, o, tm], is_nil(tm.user_id)) + |> where([thread_mute: thread_mute], is_nil(thread_mute.user_id)) end defp exclude_filtered(query, user) do @@ -358,7 +354,7 @@ def dismiss(%{id: user_id} = _user, id) do def create_notifications(activity, options \\ []) def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = activity, options) do - object = Object.normalize(activity, false) + object = Object.normalize(activity, fetch: false) if object && object.data["type"] == "Answer" do {:ok, []} @@ -507,8 +503,8 @@ def get_potential_receiver_ap_ids(%{data: %{"type" => "Follow", "object" => obje [object_id] end - def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag"}}) do - User.all_superusers() |> Enum.map(fn user -> user.ap_id end) + def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag", "actor" => actor}}) do + (User.all_superusers() |> Enum.map(fn user -> user.ap_id end)) -- [actor] end def get_potential_receiver_ap_ids(activity) do @@ -625,7 +621,7 @@ def skip?(:recently_followed, %Activity{data: %{"type" => "Follow"}} = activity, def skip?(:filtered, %{data: %{"type" => type}}, _) when type in ["Follow", "Move"], do: false def skip?(:filtered, activity, user) do - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) cond do is_nil(object) -> diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 052ad413b..3ba749d1a 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Object do @@ -10,6 +10,7 @@ defmodule Pleroma.Object do alias Pleroma.Activity alias Pleroma.Config + alias Pleroma.Hashtag alias Pleroma.Object alias Pleroma.Object.Fetcher alias Pleroma.ObjectTombstone @@ -23,9 +24,13 @@ defmodule Pleroma.Object do @derive {Jason.Encoder, only: [:data]} + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + schema "objects" do field(:data, :map) + many_to_many(:hashtags, Hashtag, join_through: "hashtags_objects", on_replace: :delete) + timestamps() end @@ -47,7 +52,8 @@ def with_joined_activity(query, activity_type \\ "Create", join_type \\ :inner) end def create(data) do - Object.change(%Object{}, %{data: data}) + %Object{} + |> Object.change(%{data: data}) |> Repo.insert() end @@ -56,8 +62,41 @@ def change(struct, params \\ %{}) do |> cast(params, [:data]) |> validate_required([:data]) |> unique_constraint(:ap_id, name: :objects_unique_apid_index) + # Expecting `maybe_handle_hashtags_change/1` to run last: + |> maybe_handle_hashtags_change(struct) end + # Note: not checking activity type (assuming non-legacy objects are associated with Create act.) + defp maybe_handle_hashtags_change(changeset, struct) do + with %Ecto.Changeset{valid?: true} <- changeset, + data_hashtags_change = get_change(changeset, :data), + {_, true} <- {:changed, hashtags_changed?(struct, data_hashtags_change)}, + {:ok, hashtag_records} <- + data_hashtags_change + |> object_data_hashtags() + |> Hashtag.get_or_create_by_names() do + put_assoc(changeset, :hashtags, hashtag_records) + else + %{valid?: false} -> + changeset + + {:changed, false} -> + changeset + + {:error, _} -> + validate_change(changeset, :data, fn _, _ -> + [data: "error referencing hashtags"] + end) + end + end + + defp hashtags_changed?(%Object{} = struct, %{"tag" => _} = data) do + Enum.sort(embedded_hashtags(struct)) != + Enum.sort(object_data_hashtags(data)) + end + + defp hashtags_changed?(_, _), do: false + def get_by_id(nil), do: nil def get_by_id(id), do: Repo.get(Object, id) @@ -106,39 +145,42 @@ defp warn_on_no_object_preloaded(ap_id) do Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}") end - def normalize(_, fetch_remote \\ true, options \\ []) + def normalize(_, options \\ [fetch: false]) # If we pass an Activity to Object.normalize(), we can try to use the preloaded object. # Use this whenever possible, especially when walking graphs in an O(N) loop! - def normalize(%Object{} = object, _, _), do: object - def normalize(%Activity{object: %Object{} = object}, _, _), do: object + def normalize(%Object{} = object, _), do: object + def normalize(%Activity{object: %Object{} = object}, _), do: object # A hack for fake activities - def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}, _, _) do + def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}, _) do %Object{id: "pleroma:fake_object_id", data: data} end # No preloaded object - def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}, fetch_remote, _) do + def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}, options) do warn_on_no_object_preloaded(ap_id) - normalize(ap_id, fetch_remote) + normalize(ap_id, options) end # No preloaded object - def normalize(%Activity{data: %{"object" => ap_id}}, fetch_remote, _) do + def normalize(%Activity{data: %{"object" => ap_id}}, options) do warn_on_no_object_preloaded(ap_id) - normalize(ap_id, fetch_remote) + normalize(ap_id, options) end # Old way, try fetching the object through cache. - def normalize(%{"id" => ap_id}, fetch_remote, _), do: normalize(ap_id, fetch_remote) - def normalize(ap_id, false, _) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id) + def normalize(%{"id" => ap_id}, options), do: normalize(ap_id, options) - def normalize(ap_id, true, options) when is_binary(ap_id) do - Fetcher.fetch_object_from_id!(ap_id, options) + def normalize(ap_id, options) when is_binary(ap_id) do + if Keyword.get(options, :fetch) do + Fetcher.fetch_object_from_id!(ap_id, options) + else + get_cached_by_ap_id(ap_id) + end end - def normalize(_, _, _), do: nil + def normalize(_, _), do: nil # Owned objects can only be accessed by their owner def authorize_access(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}) do @@ -156,9 +198,9 @@ def authorize_access(%Object{}, %User{}), do: :ok def get_cached_by_ap_id(ap_id) do key = "object:#{ap_id}" - with {:ok, nil} <- Cachex.get(:object_cache, key), + with {:ok, nil} <- @cachex.get(:object_cache, key), object when not is_nil(object) <- get_by_ap_id(ap_id), - {:ok, true} <- Cachex.put(:object_cache, key, object) do + {:ok, true} <- @cachex.put(:object_cache, key, object) do object else {:ok, object} -> object @@ -182,9 +224,13 @@ def make_tombstone(%Object{data: %{"id" => id, "type" => type}}, deleted \\ Date def swap_object_with_tombstone(object) do tombstone = make_tombstone(object) - object - |> Object.change(%{data: tombstone}) - |> Repo.update() + with {:ok, object} <- + object + |> Object.change(%{data: tombstone}) + |> Repo.update() do + Hashtag.unlink(object) + {:ok, object} + end end def delete(%Object{data: %{"id" => id}} = object) do @@ -216,13 +262,13 @@ def prune(%Object{data: %{"id" => _id}} = object) do end def invalid_object_cache(%Object{data: %{"id" => id}}) do - with {:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do - Cachex.del(:web_resp_cache, URI.parse(id).path) + with {:ok, true} <- @cachex.del(:object_cache, "object:#{id}") do + @cachex.del(:web_resp_cache, URI.parse(id).path) end end def set_cache(%Object{data: %{"id" => ap_id}} = object) do - Cachex.put(:object_cache, "object:#{ap_id}", object) + @cachex.put(:object_cache, "object:#{ap_id}", object) {:ok, object} end @@ -283,7 +329,7 @@ def decrease_replies_count(ap_id) do end def increase_vote_count(ap_id, name, actor) do - with %Object{} = object <- Object.normalize(ap_id), + with %Object{} = object <- Object.normalize(ap_id, fetch: false), "Question" <- object.data["type"] do key = if poll_is_multiple?(object), do: "anyOf", else: "oneOf" @@ -324,7 +370,7 @@ def local?(%Object{data: %{"id" => id}}) do end def replies(object, opts \\ []) do - object = Object.normalize(object) + object = Object.normalize(object, fetch: false) query = Object @@ -344,4 +390,39 @@ def replies(object, opts \\ []) do def self_replies(object, opts \\ []), do: replies(object, Keyword.put(opts, :self_only, true)) + + def tags(%Object{data: %{"tag" => tags}}) when is_list(tags), do: tags + + def tags(_), do: [] + + def hashtags(%Object{} = object) do + # Note: always using embedded hashtags regardless whether they are migrated to hashtags table + # (embedded hashtags stay in sync anyways, and we avoid extra joins and preload hassle) + embedded_hashtags(object) + end + + def embedded_hashtags(%Object{data: data}) do + object_data_hashtags(data) + end + + def embedded_hashtags(_), do: [] + + def object_data_hashtags(%{"tag" => tags}) when is_list(tags) do + tags + |> Enum.filter(fn + %{"type" => "Hashtag"} = data -> Map.has_key?(data, "name") + plain_text when is_bitstring(plain_text) -> true + _ -> false + end) + |> Enum.map(fn + %{"name" => "#" <> hashtag} -> String.downcase(hashtag) + %{"name" => hashtag} -> String.downcase(hashtag) + hashtag when is_bitstring(hashtag) -> String.downcase(hashtag) + end) + |> Enum.uniq() + # Note: "" elements (plain text) might occur in `data.tag` for incoming objects + |> Enum.filter(&(&1 not in [nil, ""])) + end + + def object_data_hashtags(_), do: [] end diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex index 29cb3d672..040537acf 100644 --- a/lib/pleroma/object/containment.ex +++ b/lib/pleroma/object/containment.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Object.Containment do @@ -71,6 +71,14 @@ def contain_origin_from_id(id, %{"id" => other_id} = _params) when is_binary(oth compare_uris(id_uri, other_uri) end + # Mastodon pin activities don't have an id, so we check the object field, which will be pinned. + def contain_origin_from_id(id, %{"object" => object}) when is_binary(object) do + id_uri = URI.parse(id) + object_uri = URI.parse(object) + + compare_uris(id_uri, object_uri) + end + def contain_origin_from_id(_id, _data), do: :error def contain_child(%{"object" => %{"id" => id, "attributedTo" => _} = object}), diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 20d8f687d..bcccf1c4c 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Object.Fetcher do @@ -83,13 +83,13 @@ def fetch_object_from_id(id, options \\ []) do with {_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)}, {_, true} <- {:allowed_depth, Federator.allowed_thread_distance?(options[:depth])}, {_, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)}, - {_, nil} <- {:normalize, Object.normalize(data, false)}, + {_, nil} <- {:normalize, Object.normalize(data, fetch: false)}, params <- prepare_activity_params(data), {_, :ok} <- {:containment, Containment.contain_origin(id, params)}, {_, {:ok, activity}} <- {:transmogrifier, Transmogrifier.handle_incoming(params, options)}, {_, _data, %Object{} = object} <- - {:object, data, Object.normalize(activity, false)} do + {:object, data, Object.normalize(activity, fetch: false)} do {:ok, object} else {:allowed_depth, false} -> diff --git a/lib/pleroma/object_tombstone.ex b/lib/pleroma/object_tombstone.ex index e26f44057..a42d2d9a0 100644 --- a/lib/pleroma/object_tombstone.ex +++ b/lib/pleroma/object_tombstone.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ObjectTombstone do diff --git a/lib/pleroma/otp_version.ex b/lib/pleroma/otp_version.ex index 114d0054f..a5ac1b072 100644 --- a/lib/pleroma/otp_version.ex +++ b/lib/pleroma/otp_version.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.OTPVersion do diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex index 9a3795769..33e45a0eb 100644 --- a/lib/pleroma/pagination.ex +++ b/lib/pleroma/pagination.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Pagination do @@ -93,6 +93,7 @@ defp cast_params(params) do max_id: :string, offset: :integer, limit: :integer, + skip_extra_order: :boolean, skip_order: :boolean } @@ -114,6 +115,8 @@ defp restrict(query, :max_id, %{max_id: max_id}, table_binding) do defp restrict(query, :order, %{skip_order: true}, _), do: query + defp restrict(%{order_bys: [_ | _]} = query, :order, %{skip_extra_order: true}, _), do: query + defp restrict(query, :order, %{min_id: _}, table_binding) do order_by( query, diff --git a/lib/pleroma/password/pbkdf2.ex b/lib/pleroma/password/pbkdf2.ex new file mode 100644 index 000000000..2fd5f4491 --- /dev/null +++ b/lib/pleroma/password/pbkdf2.ex @@ -0,0 +1,55 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Password.Pbkdf2 do + @moduledoc """ + This module implements Pbkdf2 passwords in terms of Plug.Crypto. + """ + + alias Plug.Crypto.KeyGenerator + + def decode64(str) do + str + |> String.replace(".", "+") + |> Base.decode64!(padding: false) + end + + def encode64(bin) do + bin + |> Base.encode64(padding: false) + |> String.replace("+", ".") + end + + def verify_pass(password, hash) do + ["pbkdf2-" <> digest, iterations, salt, hash] = String.split(hash, "$", trim: true) + + salt = decode64(salt) + + iterations = String.to_integer(iterations) + + digest = String.to_atom(digest) + + binary_hash = + KeyGenerator.generate(password, salt, digest: digest, iterations: iterations, length: 64) + + encode64(binary_hash) == hash + end + + def hash_pwd_salt(password, opts \\ []) do + salt = + Keyword.get_lazy(opts, :salt, fn -> + :crypto.strong_rand_bytes(16) + end) + + digest = Keyword.get(opts, :digest, :sha512) + + iterations = + Keyword.get(opts, :iterations, Pleroma.Config.get([:password, :iterations], 160_000)) + + binary_hash = + KeyGenerator.generate(password, salt, digest: digest, iterations: iterations, length: 64) + + "$pbkdf2-#{digest}$#{iterations}$#{encode64(salt)}$#{encode64(binary_hash)}" + end +end diff --git a/lib/pleroma/password_reset_token.ex b/lib/pleroma/password_reset_token.ex index fea5b1c22..edc8ed6a0 100644 --- a/lib/pleroma/password_reset_token.ex +++ b/lib/pleroma/password_reset_token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.PasswordResetToken do diff --git a/lib/pleroma/registration.ex b/lib/pleroma/registration.ex index 9163040b4..7b49618e1 100644 --- a/lib/pleroma/registration.ex +++ b/lib/pleroma/registration.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Registration do diff --git a/lib/pleroma/release_tasks.ex b/lib/pleroma/release_tasks.ex index 02dd6c325..1e06aafe4 100644 --- a/lib/pleroma/release_tasks.ex +++ b/lib/pleroma/release_tasks.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReleaseTasks do diff --git a/lib/pleroma/repo.ex b/lib/pleroma/repo.ex index 4524bd5e2..b8ea06e33 100644 --- a/lib/pleroma/repo.ex +++ b/lib/pleroma/repo.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Repo do @@ -8,6 +8,8 @@ defmodule Pleroma.Repo do adapter: Ecto.Adapters.Postgres, migration_timestamps: [type: :naive_datetime_usec] + use Ecto.Explain + import Ecto.Query require Logger @@ -63,8 +65,8 @@ def get_assoc(resource, association) do iex> Pleroma.Repo.chunk_stream(Pleroma.Activity.Queries.by_actor(ap_id), 500, :batches) """ @spec chunk_stream(Ecto.Query.t(), integer(), atom()) :: Enumerable.t() - def chunk_stream(query, chunk_size, returns_as \\ :one) do - # We don't actually need start and end funcitons of resource streaming, + def chunk_stream(query, chunk_size, returns_as \\ :one, query_options \\ []) do + # We don't actually need start and end functions of resource streaming, # but it seems to be the only way to not fetch records one-by-one and # have individual records be the elements of the stream, instead of # lists of records @@ -76,7 +78,7 @@ def chunk_stream(query, chunk_size, returns_as \\ :one) do |> order_by(asc: :id) |> where([r], r.id > ^last_id) |> limit(^chunk_size) - |> all() + |> all(query_options) |> case do [] -> {:halt, last_id} diff --git a/lib/pleroma/report_note.ex b/lib/pleroma/report_note.ex index a239bd361..f8bab1548 100644 --- a/lib/pleroma/report_note.ex +++ b/lib/pleroma/report_note.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReportNote do diff --git a/lib/pleroma/reverse_proxy.ex b/lib/pleroma/reverse_proxy.ex index 8ae1157df..406f7e2b8 100644 --- a/lib/pleroma/reverse_proxy.ex +++ b/lib/pleroma/reverse_proxy.ex @@ -1,10 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReverseProxy do @range_headers ~w(range if-range) - @keep_req_headers ~w(accept user-agent accept-encoding cache-control if-modified-since) ++ + @keep_req_headers ~w(accept accept-encoding cache-control if-modified-since) ++ ~w(if-unmodified-since if-none-match) ++ @range_headers @resp_cache_headers ~w(etag date last-modified) @keep_resp_headers @resp_cache_headers ++ @@ -17,6 +17,8 @@ defmodule Pleroma.ReverseProxy do @failed_request_ttl :timer.seconds(60) @methods ~w(GET HEAD) + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + def max_read_duration_default, do: @max_read_duration def default_cache_control_header, do: @default_cache_control_header @@ -55,9 +57,6 @@ def default_cache_control_header, do: @default_cache_control_header * `false` will add `content-disposition: attachment` to any request, * a list of whitelisted content types - * `keep_user_agent` will forward the client's user-agent to the upstream. This may be useful if the upstream is - doing content transformation (encoding, …) depending on the request. - * `req_headers`, `resp_headers` additional headers. * `http`: options for [hackney](https://github.com/benoitc/hackney) or [gun](https://github.com/ninenines/gun). @@ -82,8 +81,7 @@ def default_cache_control_header, do: @default_cache_control_header import Plug.Conn @type option() :: - {:keep_user_agent, boolean} - | {:max_read_duration, :timer.time() | :infinity} + {:max_read_duration, :timer.time() | :infinity} | {:max_body_length, non_neg_integer() | :infinity} | {:failed_request_ttl, :timer.time() | :infinity} | {:http, []} @@ -107,7 +105,7 @@ def call(conn = %{method: method}, url, opts) when method in @methods do opts end - with {:ok, nil} <- Cachex.get(:failed_proxy_url_cache, url), + with {:ok, nil} <- @cachex.get(:failed_proxy_url_cache, url), {:ok, code, headers, client} <- request(method, url, req_headers, client_opts), :ok <- header_length_constraint( @@ -289,17 +287,13 @@ defp build_req_range_or_encoding_header(headers, _opts) do end end - defp build_req_user_agent_header(headers, opts) do - if Keyword.get(opts, :keep_user_agent, false) do - List.keystore( - headers, - "user-agent", - 0, - {"user-agent", Pleroma.Application.user_agent()} - ) - else - headers - end + defp build_req_user_agent_header(headers, _opts) do + List.keystore( + headers, + "user-agent", + 0, + {"user-agent", Pleroma.Application.user_agent()} + ) end defp build_resp_headers(headers, opts) do @@ -427,6 +421,6 @@ defp track_failed_url(url, error, opts) do nil end - Cachex.put(:failed_proxy_url_cache, url, true, ttl: ttl) + @cachex.put(:failed_proxy_url_cache, url, true, ttl: ttl) end end diff --git a/lib/pleroma/reverse_proxy/client.ex b/lib/pleroma/reverse_proxy/client.ex index 0d13ff174..8698fa2e1 100644 --- a/lib/pleroma/reverse_proxy/client.ex +++ b/lib/pleroma/reverse_proxy/client.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReverseProxy.Client do diff --git a/lib/pleroma/reverse_proxy/client/hackney.ex b/lib/pleroma/reverse_proxy/client/hackney.ex index ad988fac3..dba946308 100644 --- a/lib/pleroma/reverse_proxy/client/hackney.ex +++ b/lib/pleroma/reverse_proxy/client/hackney.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReverseProxy.Client.Hackney do diff --git a/lib/pleroma/reverse_proxy/client/tesla.ex b/lib/pleroma/reverse_proxy/client/tesla.ex index 4b118eec2..36a0a2060 100644 --- a/lib/pleroma/reverse_proxy/client/tesla.ex +++ b/lib/pleroma/reverse_proxy/client/tesla.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReverseProxy.Client.Tesla do diff --git a/lib/pleroma/scheduled_activity.ex b/lib/pleroma/scheduled_activity.ex index 0937cb7db..2b156341f 100644 --- a/lib/pleroma/scheduled_activity.ex +++ b/lib/pleroma/scheduled_activity.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ScheduledActivity do diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index 3aa6909d2..43ab569a4 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Signature do diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex index 48afe901e..3e3f24c2c 100644 --- a/lib/pleroma/stats.ex +++ b/lib/pleroma/stats.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Stats do @@ -23,7 +23,11 @@ def start_link(_) do @impl true def init(_args) do - {:ok, nil, {:continue, :calculate_stats}} + if Pleroma.Config.get(:env) != :test do + {:ok, nil, {:continue, :calculate_stats}} + else + {:ok, calculate_stat_data()} + end end @doc "Performs update stats" @@ -75,7 +79,7 @@ def calculate_stat_data do users_query = from(u in User, - where: u.deactivated != true, + where: u.is_active == true, where: u.local == true, where: not is_nil(u.nickname), where: not u.invisible diff --git a/lib/pleroma/telemetry/logger.ex b/lib/pleroma/telemetry/logger.ex index 003079cf3..44d2f48dc 100644 --- a/lib/pleroma/telemetry/logger.ex +++ b/lib/pleroma/telemetry/logger.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Telemetry.Logger do diff --git a/lib/pleroma/tesla/middleware/connection_pool.ex b/lib/pleroma/tesla/middleware/connection_pool.ex index 056e736ce..906706d39 100644 --- a/lib/pleroma/tesla/middleware/connection_pool.ex +++ b/lib/pleroma/tesla/middleware/connection_pool.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Tesla.Middleware.ConnectionPool do diff --git a/lib/pleroma/tests/auth_test_controller.ex b/lib/pleroma/tests/auth_test_controller.ex index b30d83567..ddf3fea4f 100644 --- a/lib/pleroma/tests/auth_test_controller.ex +++ b/lib/pleroma/tests/auth_test_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only # A test controller reachable only in :test env. diff --git a/lib/pleroma/thread_mute.ex b/lib/pleroma/thread_mute.ex index be01d541d..5d06cf030 100644 --- a/lib/pleroma/thread_mute.ex +++ b/lib/pleroma/thread_mute.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ThreadMute do diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index db2cc1dae..654711351 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload do @@ -31,6 +31,7 @@ defmodule Pleroma.Upload do """ alias Ecto.UUID + alias Pleroma.Config require Logger @type source :: @@ -130,12 +131,7 @@ defp get_opts(opts) do uploader: Keyword.get(opts, :uploader, Pleroma.Config.get([__MODULE__, :uploader])), filters: Keyword.get(opts, :filters, Pleroma.Config.get([__MODULE__, :filters])), description: Keyword.get(opts, :description), - base_url: - Keyword.get( - opts, - :base_url, - Pleroma.Config.get([__MODULE__, :base_url], Pleroma.Web.base_url()) - ) + base_url: base_url() } end @@ -216,16 +212,46 @@ defp url_from_spec(%__MODULE__{name: name}, base_url, {:file, path}) do "" end - prefix = - if is_nil(Pleroma.Config.get([__MODULE__, :base_url])) do - "media" - else - "" - end - - [base_url, prefix, path] + [base_url, path] |> Path.join() end defp url_from_spec(_upload, _base_url, {:url, url}), do: url + + def base_url do + uploader = Config.get([Pleroma.Upload, :uploader]) + upload_base_url = Config.get([Pleroma.Upload, :base_url]) + public_endpoint = Config.get([uploader, :public_endpoint]) + + case uploader do + Pleroma.Uploaders.Local -> + upload_base_url || Pleroma.Web.base_url() <> "/media/" + + Pleroma.Uploaders.S3 -> + bucket = Config.get([Pleroma.Uploaders.S3, :bucket]) + truncated_namespace = Config.get([Pleroma.Uploaders.S3, :truncated_namespace]) + namespace = Config.get([Pleroma.Uploaders.S3, :bucket_namespace]) + + bucket_with_namespace = + cond do + !is_nil(truncated_namespace) -> + truncated_namespace + + !is_nil(namespace) -> + namespace <> ":" <> bucket + + true -> + bucket + end + + if public_endpoint do + Path.join([public_endpoint, bucket_with_namespace]) + else + Path.join([upload_base_url, bucket_with_namespace]) + end + + _ -> + public_endpoint || upload_base_url || Pleroma.Web.base_url() <> "/media/" + end + end end diff --git a/lib/pleroma/upload/filter.ex b/lib/pleroma/upload/filter.ex index 661135634..c677d4b9f 100644 --- a/lib/pleroma/upload/filter.ex +++ b/lib/pleroma/upload/filter.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter do diff --git a/lib/pleroma/upload/filter/anonymize_filename.ex b/lib/pleroma/upload/filter/anonymize_filename.ex index fc456e4f4..7e62b3d13 100644 --- a/lib/pleroma/upload/filter/anonymize_filename.ex +++ b/lib/pleroma/upload/filter/anonymize_filename.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.AnonymizeFilename do diff --git a/lib/pleroma/upload/filter/dedupe.ex b/lib/pleroma/upload/filter/dedupe.ex index 86cbc8996..2bf581b05 100644 --- a/lib/pleroma/upload/filter/dedupe.ex +++ b/lib/pleroma/upload/filter/dedupe.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.Dedupe do diff --git a/lib/pleroma/upload/filter/exiftool.ex b/lib/pleroma/upload/filter/exiftool.ex index 1fd0cfdaa..a2bfbbf61 100644 --- a/lib/pleroma/upload/filter/exiftool.ex +++ b/lib/pleroma/upload/filter/exiftool.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.Exiftool do @@ -11,7 +11,8 @@ defmodule Pleroma.Upload.Filter.Exiftool do @spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()} - # webp is not compatible with exiftool at this time + # Formats not compatible with exiftool at this time + def filter(%Pleroma.Upload{content_type: "image/heic"}), do: {:ok, :noop} def filter(%Pleroma.Upload{content_type: "image/webp"}), do: {:ok, :noop} def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do @@ -21,8 +22,8 @@ def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do {error, 1} -> {:error, error} end rescue - _e in ErlangError -> - {:error, "exiftool command not found"} + e in ErlangError -> + {:error, "#{__MODULE__}: #{inspect(e)}"} end end diff --git a/lib/pleroma/upload/filter/mogrifun.ex b/lib/pleroma/upload/filter/mogrifun.ex index 363e5cf0f..01126aaeb 100644 --- a/lib/pleroma/upload/filter/mogrifun.ex +++ b/lib/pleroma/upload/filter/mogrifun.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.Mogrifun do @@ -44,8 +44,8 @@ def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do Filter.Mogrify.do_filter(file, [Enum.random(@filters)]) {:ok, :filtered} rescue - _e in ErlangError -> - {:error, "mogrify command not found"} + e in ErlangError -> + {:error, "#{__MODULE__}: #{inspect(e)}"} end end diff --git a/lib/pleroma/upload/filter/mogrify.ex b/lib/pleroma/upload/filter/mogrify.ex index 71968fd9c..f27aefc22 100644 --- a/lib/pleroma/upload/filter/mogrify.ex +++ b/lib/pleroma/upload/filter/mogrify.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.Mogrify do @@ -14,8 +14,8 @@ def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do do_filter(file, Pleroma.Config.get!([__MODULE__, :args])) {:ok, :filtered} rescue - _e in ErlangError -> - {:error, "mogrify command not found"} + e in ErlangError -> + {:error, "#{__MODULE__}: #{inspect(e)}"} end end diff --git a/lib/pleroma/uploaders/local.ex b/lib/pleroma/uploaders/local.ex index 10b3069f4..0e1ba4b90 100644 --- a/lib/pleroma/uploaders/local.ex +++ b/lib/pleroma/uploaders/local.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Uploaders.Local do diff --git a/lib/pleroma/uploaders/s3.ex b/lib/pleroma/uploaders/s3.ex index 6dbef9085..d85c8cb2f 100644 --- a/lib/pleroma/uploaders/s3.ex +++ b/lib/pleroma/uploaders/s3.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Uploaders.S3 do @@ -12,26 +12,10 @@ defmodule Pleroma.Uploaders.S3 do # links with less strict filenames @impl true def get_file(file) do - config = Config.get([__MODULE__]) - bucket = Keyword.fetch!(config, :bucket) - - bucket_with_namespace = - cond do - truncated_namespace = Keyword.get(config, :truncated_namespace) -> - truncated_namespace - - namespace = Keyword.get(config, :bucket_namespace) -> - namespace <> ":" <> bucket - - true -> - bucket - end - {:ok, {:url, Path.join([ - Keyword.fetch!(config, :public_endpoint), - bucket_with_namespace, + Pleroma.Upload.base_url(), strict_encode(URI.decode(file)) ])}} end diff --git a/lib/pleroma/uploaders/uploader.ex b/lib/pleroma/uploaders/uploader.ex index 6249eceb1..0be878ca2 100644 --- a/lib/pleroma/uploaders/uploader.ex +++ b/lib/pleroma/uploaders/uploader.ex @@ -1,10 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Uploaders.Uploader do import Pleroma.Web.Gettext + @mix_env Mix.env() + @moduledoc """ Defines the contract to put and get an uploaded file to any backend. """ @@ -74,7 +76,7 @@ defp handle_callback(uploader, upload) do end defp callback_timeout do - case Mix.env() do + case @mix_env do :test -> 1_000 _ -> 30_000 end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 1836643a6..b78777141 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User do @@ -81,6 +81,8 @@ defmodule Pleroma.User do ] ] + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + schema "users" do field(:bio, :string, default: "") field(:raw_bio, :string) @@ -97,6 +99,7 @@ defmodule Pleroma.User do field(:local, :boolean, default: true) field(:follower_address, :string) field(:following_address, :string) + field(:featured_address, :string) field(:search_rank, :float, virtual: true) field(:search_type, :integer, virtual: true) field(:tags, {:array, :string}, default: []) @@ -108,14 +111,14 @@ defmodule Pleroma.User do field(:follower_count, :integer, default: 0) field(:following_count, :integer, default: 0) field(:is_locked, :boolean, default: false) - field(:confirmation_pending, :boolean, default: false) + field(:is_confirmed, :boolean, default: true) field(:password_reset_pending, :boolean, default: false) - field(:approval_pending, :boolean, default: false) + field(:is_approved, :boolean, default: true) field(:registration_reason, :string, default: nil) field(:confirmation_token, :string, default: nil) field(:default_scope, :string, default: "public") field(:domain_blocks, {:array, :string}, default: []) - field(:deactivated, :boolean, default: false) + field(:is_active, :boolean, default: true) field(:no_rich_text, :boolean, default: false) field(:ap_enabled, :boolean, default: false) field(:is_moderator, :boolean, default: false) @@ -128,7 +131,6 @@ defmodule Pleroma.User do field(:hide_followers, :boolean, default: false) field(:hide_follows, :boolean, default: false) field(:hide_favorites, :boolean, default: true) - field(:pinned_activities, {:array, :string}, default: []) field(:email_notifications, :map, default: %{"digest" => false}) field(:mascot, :map, default: nil) field(:emoji, :map, default: %{}) @@ -140,10 +142,13 @@ defmodule Pleroma.User do field(:allow_following_move, :boolean, default: true) field(:skip_thread_containment, :boolean, default: false) field(:actor_type, :string, default: "Person") - field(:also_known_as, {:array, :string}, default: []) + field(:also_known_as, {:array, ObjectValidators.ObjectID}, default: []) field(:inbox, :string) field(:shared_inbox, :string) field(:accepts_chat_messages, :boolean, default: nil) + field(:last_active_at, :naive_datetime) + field(:disclose_client, :boolean, default: true) + field(:pinned_objects, :map, default: %{}) embeds_one( :notification_settings, @@ -215,7 +220,8 @@ def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? target_users_query = assoc(user, unquote(outgoing_relation_target)) if restrict_deactivated? do - restrict_deactivated(target_users_query) + target_users_query + |> User.Query.build(%{deactivated: false}) else target_users_query end @@ -246,13 +252,13 @@ def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \ end def cached_blocked_users_ap_ids(user) do - Cachex.fetch!(:user_cache, "blocked_users_ap_ids:#{user.ap_id}", fn _ -> + @cachex.fetch!(:user_cache, "blocked_users_ap_ids:#{user.ap_id}", fn _ -> blocked_users_ap_ids(user) end) end def cached_muted_users_ap_ids(user) do - Cachex.fetch!(:user_cache, "muted_users_ap_ids:#{user.ap_id}", fn _ -> + @cachex.fetch!(:user_cache, "muted_users_ap_ids:#{user.ap_id}", fn _ -> muted_users_ap_ids(user) end) end @@ -284,18 +290,10 @@ def binary_id(%User{} = user), do: binary_id(user.id) @doc "Returns status account" @spec account_status(User.t()) :: account_status() - def account_status(%User{deactivated: true}), do: :deactivated + def account_status(%User{is_active: false}), do: :deactivated def account_status(%User{password_reset_pending: true}), do: :password_reset_pending - def account_status(%User{local: true, approval_pending: true}), do: :approval_pending - - def account_status(%User{local: true, confirmation_pending: true}) do - if Config.get([:instance, :account_activation_required]) do - :confirmation_pending - else - :active - end - end - + def account_status(%User{local: true, is_approved: false}), do: :approval_pending + def account_status(%User{local: true, is_confirmed: false}), do: :confirmation_pending def account_status(%User{}), do: :active @spec visible_for(User.t(), User.t() | nil) :: @@ -375,8 +373,10 @@ def banner_url(user, options \\ []) do end # Should probably be renamed or removed + @spec ap_id(User.t()) :: String.t() def ap_id(%User{nickname: nickname}), do: "#{Web.base_url()}/users/#{nickname}" + @spec ap_followers(User.t()) :: String.t() def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers" @@ -384,10 +384,10 @@ def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers" def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa def ap_following(%User{} = user), do: "#{ap_id(user)}/following" - @spec restrict_deactivated(Ecto.Query.t()) :: Ecto.Query.t() - def restrict_deactivated(query) do - from(u in query, where: u.deactivated != ^true) - end + @spec ap_featured_collection(User.t()) :: String.t() + def ap_featured_collection(%User{featured_address: fa}) when is_binary(fa), do: fa + + def ap_featured_collection(%User{} = user), do: "#{ap_id(user)}/collections/featured" defp truncate_fields_param(params) do if Map.has_key?(params, :fields) do @@ -451,6 +451,7 @@ def remote_user_changeset(struct \\ %User{local: false}, params) do :uri, :follower_address, :following_address, + :featured_address, :hide_followers, :hide_follows, :hide_followers_count, @@ -462,7 +463,8 @@ def remote_user_changeset(struct \\ %User{local: false}, params) do :invisible, :actor_type, :also_known_as, - :accepts_chat_messages + :accepts_chat_messages, + :pinned_objects ] ) |> cast(params, [:name], empty_values: []) @@ -513,6 +515,7 @@ def update_changeset(struct, params \\ %{}) do :hide_follows_count, :hide_favorites, :allow_following_move, + :also_known_as, :background, :show_role, :skip_thread_containment, @@ -521,8 +524,8 @@ def update_changeset(struct, params \\ %{}) do :pleroma_settings_store, :is_discoverable, :actor_type, - :also_known_as, - :accepts_chat_messages + :accepts_chat_messages, + :disclose_client ] ) |> unique_constraint(:nickname) @@ -693,7 +696,7 @@ def register_changeset_ldap(struct, params = %{password: password}) |> validate_format(:nickname, local_nickname_regex()) |> put_ap_id() |> unique_constraint(:ap_id) - |> put_following_and_follower_address() + |> put_following_and_follower_and_featured_address() end def register_changeset(struct, params \\ %{}, opts \\ []) do @@ -702,23 +705,23 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do reason_limit = Config.get([:instance, :registration_reason_length], 500) params = Map.put_new(params, :accepts_chat_messages, true) - need_confirmation? = - if is_nil(opts[:need_confirmation]) do - Config.get([:instance, :account_activation_required]) + confirmed? = + if is_nil(opts[:confirmed]) do + !Config.get([:instance, :account_activation_required]) else - opts[:need_confirmation] + opts[:confirmed] end - need_approval? = - if is_nil(opts[:need_approval]) do - Config.get([:instance, :account_approval_required]) + approved? = + if is_nil(opts[:approved]) do + !Config.get([:instance, :account_approval_required]) else - opts[:need_approval] + opts[:approved] end struct - |> confirmation_changeset(need_confirmation: need_confirmation?) - |> approval_changeset(need_approval: need_approval?) + |> confirmation_changeset(set_confirmation: confirmed?) + |> approval_changeset(set_approval: approved?) |> cast(params, [ :bio, :raw_bio, @@ -754,7 +757,7 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do |> put_password_hash |> put_ap_id() |> unique_constraint(:ap_id) - |> put_following_and_follower_address() + |> put_following_and_follower_and_featured_address() end def maybe_validate_required_email(changeset, true), do: changeset @@ -772,18 +775,23 @@ defp put_ap_id(changeset) do put_change(changeset, :ap_id, ap_id) end - defp put_following_and_follower_address(changeset) do - followers = ap_followers(%User{nickname: get_field(changeset, :nickname)}) + defp put_following_and_follower_and_featured_address(changeset) do + user = %User{nickname: get_field(changeset, :nickname)} + followers = ap_followers(user) + following = ap_following(user) + featured = ap_featured_collection(user) changeset |> put_change(:follower_address, followers) + |> put_change(:following_address, following) + |> put_change(:featured_address, featured) end defp autofollow_users(user) do candidates = Config.get([:instance, :autofollowed_nicknames]) autofollowed_users = - User.Query.build(%{nickname: candidates, local: true, deactivated: false}) + User.Query.build(%{nickname: candidates, local: true, is_active: true}) |> Repo.all() follow_all(user, autofollowed_users) @@ -806,19 +814,52 @@ def register(%Ecto.Changeset{} = changeset) do end end - def post_register_action(%User{} = user) do - with {:ok, user} <- autofollow_users(user), - {:ok, _} <- autofollowing_users(user), - {:ok, user} <- set_cache(user), - {:ok, _} <- send_welcome_email(user), - {:ok, _} <- send_welcome_message(user), - {:ok, _} <- send_welcome_chat_message(user), - {:ok, _} <- try_send_confirmation_email(user) do + def post_register_action(%User{is_confirmed: false} = user) do + with {:ok, _} <- maybe_send_confirmation_email(user) do {:ok, user} end end - def send_welcome_message(user) do + def post_register_action(%User{is_approved: false} = user) do + with {:ok, _} <- send_user_approval_email(user), + {:ok, _} <- send_admin_approval_emails(user) do + {:ok, user} + end + end + + def post_register_action(%User{is_approved: true, is_confirmed: true} = user) do + with {:ok, user} <- autofollow_users(user), + {:ok, _} <- autofollowing_users(user), + {:ok, user} <- set_cache(user), + {:ok, _} <- maybe_send_registration_email(user), + {:ok, _} <- maybe_send_welcome_email(user), + {:ok, _} <- maybe_send_welcome_message(user), + {:ok, _} <- maybe_send_welcome_chat_message(user) do + {:ok, user} + end + end + + defp send_user_approval_email(user) do + user + |> Pleroma.Emails.UserEmail.approval_pending_email() + |> Pleroma.Emails.Mailer.deliver_async() + + {:ok, :enqueued} + end + + defp send_admin_approval_emails(user) do + all_superusers() + |> Enum.filter(fn user -> not is_nil(user.email) end) + |> Enum.each(fn superuser -> + superuser + |> Pleroma.Emails.AdminEmail.new_unapproved_registration(user) + |> Pleroma.Emails.Mailer.deliver_async() + end) + + {:ok, :enqueued} + end + + defp maybe_send_welcome_message(user) do if User.WelcomeMessage.enabled?() do User.WelcomeMessage.post_message(user) {:ok, :enqueued} @@ -827,7 +868,7 @@ def send_welcome_message(user) do end end - def send_welcome_chat_message(user) do + defp maybe_send_welcome_chat_message(user) do if User.WelcomeChatMessage.enabled?() do User.WelcomeChatMessage.post_message(user) {:ok, :enqueued} @@ -836,7 +877,7 @@ def send_welcome_chat_message(user) do end end - def send_welcome_email(%User{email: email} = user) when is_binary(email) do + defp maybe_send_welcome_email(%User{email: email} = user) when is_binary(email) do if User.WelcomeEmail.enabled?() do User.WelcomeEmail.send_email(user) {:ok, :enqueued} @@ -845,10 +886,10 @@ def send_welcome_email(%User{email: email} = user) when is_binary(email) do end end - def send_welcome_email(_), do: {:ok, :noop} + defp maybe_send_welcome_email(_), do: {:ok, :noop} - @spec try_send_confirmation_email(User.t()) :: {:ok, :enqueued | :noop} - def try_send_confirmation_email(%User{confirmation_pending: true, email: email} = user) + @spec maybe_send_confirmation_email(User.t()) :: {:ok, :enqueued | :noop} + def maybe_send_confirmation_email(%User{is_confirmed: false, email: email} = user) when is_binary(email) do if Config.get([:instance, :account_activation_required]) do send_confirmation_email(user) @@ -858,7 +899,7 @@ def try_send_confirmation_email(%User{confirmation_pending: true, email: email} end end - def try_send_confirmation_email(_), do: {:ok, :noop} + def maybe_send_confirmation_email(_), do: {:ok, :noop} @spec send_confirmation_email(Uset.t()) :: User.t() def send_confirmation_email(%User{} = user) do @@ -869,6 +910,24 @@ def send_confirmation_email(%User{} = user) do user end + @spec maybe_send_registration_email(User.t()) :: {:ok, :enqueued | :noop} + defp maybe_send_registration_email(%User{email: email} = user) when is_binary(email) do + with false <- User.WelcomeEmail.enabled?(), + false <- Config.get([:instance, :account_activation_required], false), + false <- Config.get([:instance, :account_approval_required], false) do + user + |> Pleroma.Emails.UserEmail.successful_registration_email() + |> Pleroma.Emails.Mailer.deliver_async() + + {:ok, :enqueued} + else + _ -> + {:ok, :noop} + end + end + + defp maybe_send_registration_email(_), do: {:ok, :noop} + def needs_update?(%User{local: true}), do: false def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true @@ -912,7 +971,7 @@ def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do deny_follow_blocked = Config.get([:user, :deny_follow_blocked]) cond do - followed.deactivated -> + not followed.is_active -> {:error, "Could not follow user: #{followed.nickname} is deactivated."} deny_follow_blocked and blocks?(followed, follower) -> @@ -1016,9 +1075,9 @@ def set_cache({:ok, user}), do: set_cache(user) def set_cache({:error, err}), do: {:error, err} def set_cache(%User{} = user) do - Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user) - Cachex.put(:user_cache, "nickname:#{user.nickname}", user) - Cachex.put(:user_cache, "friends_ap_ids:#{user.nickname}", get_user_friends_ap_ids(user)) + @cachex.put(:user_cache, "ap_id:#{user.ap_id}", user) + @cachex.put(:user_cache, "nickname:#{user.nickname}", user) + @cachex.put(:user_cache, "friends_ap_ids:#{user.nickname}", get_user_friends_ap_ids(user)) {:ok, user} end @@ -1041,26 +1100,26 @@ def get_user_friends_ap_ids(user) do @spec get_cached_user_friends_ap_ids(User.t()) :: [String.t()] def get_cached_user_friends_ap_ids(user) do - Cachex.fetch!(:user_cache, "friends_ap_ids:#{user.ap_id}", fn _ -> + @cachex.fetch!(:user_cache, "friends_ap_ids:#{user.ap_id}", fn _ -> get_user_friends_ap_ids(user) end) end def invalidate_cache(user) do - Cachex.del(:user_cache, "ap_id:#{user.ap_id}") - Cachex.del(:user_cache, "nickname:#{user.nickname}") - Cachex.del(:user_cache, "friends_ap_ids:#{user.ap_id}") - Cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}") - Cachex.del(:user_cache, "muted_users_ap_ids:#{user.ap_id}") + @cachex.del(:user_cache, "ap_id:#{user.ap_id}") + @cachex.del(:user_cache, "nickname:#{user.nickname}") + @cachex.del(:user_cache, "friends_ap_ids:#{user.ap_id}") + @cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}") + @cachex.del(:user_cache, "muted_users_ap_ids:#{user.ap_id}") end @spec get_cached_by_ap_id(String.t()) :: User.t() | nil def get_cached_by_ap_id(ap_id) do key = "ap_id:#{ap_id}" - with {:ok, nil} <- Cachex.get(:user_cache, key), + with {:ok, nil} <- @cachex.get(:user_cache, key), user when not is_nil(user) <- get_by_ap_id(ap_id), - {:ok, true} <- Cachex.put(:user_cache, key, user) do + {:ok, true} <- @cachex.put(:user_cache, key, user) do user else {:ok, user} -> user @@ -1072,11 +1131,11 @@ def get_cached_by_id(id) do key = "id:#{id}" ap_id = - Cachex.fetch!(:user_cache, key, fn _ -> + @cachex.fetch!(:user_cache, key, fn _ -> user = get_by_id(id) if user do - Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user) + @cachex.put(:user_cache, "ap_id:#{user.ap_id}", user) {:commit, user.ap_id} else {:ignore, ""} @@ -1089,7 +1148,7 @@ def get_cached_by_id(id) do def get_cached_by_nickname(nickname) do key = "nickname:#{nickname}" - Cachex.fetch!(:user_cache, key, fn -> + @cachex.fetch!(:user_cache, key, fn _ -> case get_or_fetch_by_nickname(nickname) do {:ok, user} -> {:commit, user} {:error, _error} -> {:ignore, nil} @@ -1147,7 +1206,7 @@ def get_or_fetch_by_nickname(nickname) do @spec get_followers_query(User.t(), pos_integer() | nil) :: Ecto.Query.t() def get_followers_query(%User{} = user, nil) do - User.Query.build(%{followers: user, deactivated: false}) + User.Query.build(%{followers: user, is_active: true}) end def get_followers_query(%User{} = user, page) do @@ -1323,7 +1382,7 @@ def update_following_count(%User{local: true} = user) do @spec get_users_from_set([String.t()], keyword()) :: [User.t()] def get_users_from_set(ap_ids, opts \\ []) do local_only = Keyword.get(opts, :local_only, true) - criteria = %{ap_id: ap_ids, deactivated: false} + criteria = %{ap_id: ap_ids, is_active: true} criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria User.Query.build(criteria) @@ -1334,7 +1393,7 @@ def get_users_from_set(ap_ids, opts \\ []) do def get_recipients_from_activity(%Activity{recipients: to, actor: actor}) do to = [actor | to] - query = User.Query.build(%{recipients_from_activity: to, local: true, deactivated: false}) + query = User.Query.build(%{recipients_from_activity: to, local: true, is_active: true}) query |> Repo.all() @@ -1358,7 +1417,7 @@ def mute(%User{} = muter, %User{} = mutee, params \\ %{}) do ) end - Cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}") + @cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}") {:ok, Enum.filter([user_mute, user_notification_mute], & &1)} end @@ -1368,7 +1427,7 @@ def unmute(%User{} = muter, %User{} = mutee) do with {:ok, user_mute} <- UserRelationship.delete_mute(muter, mutee), {:ok, user_notification_mute} <- UserRelationship.delete_notification_mute(muter, mutee) do - Cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}") + @cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}") {:ok, [user_mute, user_notification_mute]} end end @@ -1553,19 +1612,19 @@ defp maybe_filter_on_ap_id(query, ap_ids) when is_list(ap_ids) do defp maybe_filter_on_ap_id(query, _ap_ids), do: query - def deactivate_async(user, status \\ true) do - BackgroundWorker.enqueue("deactivate_user", %{"user_id" => user.id, "status" => status}) + def set_activation_async(user, status \\ true) do + BackgroundWorker.enqueue("user_activation", %{"user_id" => user.id, "status" => status}) end - def deactivate(user, status \\ true) - - def deactivate(users, status) when is_list(users) do + @spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Changeset.t()} + def set_activation(users, status) when is_list(users) do Repo.transaction(fn -> - for user <- users, do: deactivate(user, status) + for user <- users, do: set_activation(user, status) end) end - def deactivate(%User{} = user, status) do + @spec set_activation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()} + def set_activation(%User{} = user, status) do with {:ok, user} <- set_activation_status(user, status) do user |> get_followers() @@ -1590,11 +1649,34 @@ def approve(users) when is_list(users) do end) end - def approve(%User{} = user) do - change(user, approval_pending: false) - |> update_and_set_cache() + def approve(%User{is_approved: false} = user) do + with chg <- change(user, is_approved: true), + {:ok, user} <- update_and_set_cache(chg) do + post_register_action(user) + {:ok, user} + end end + def approve(%User{} = user), do: {:ok, user} + + def confirm(users) when is_list(users) do + Repo.transaction(fn -> + Enum.map(users, fn user -> + with {:ok, user} <- confirm(user), do: user + end) + end) + end + + def confirm(%User{is_confirmed: false} = user) do + with chg <- confirmation_changeset(user, set_confirmation: true), + {:ok, user} <- update_and_set_cache(chg) do + post_register_action(user) + {:ok, user} + end + end + + def confirm(%User{} = user), do: {:ok, user} + def update_notification_settings(%User{} = user, settings) do user |> cast(%{notification_settings: settings}, []) @@ -1625,13 +1707,13 @@ def purge_user_changeset(user) do follower_count: 0, following_count: 0, is_locked: false, - confirmation_pending: false, + is_confirmed: true, password_reset_pending: false, - approval_pending: false, + is_approved: true, registration_reason: nil, confirmation_token: nil, domain_blocks: [], - deactivated: true, + is_active: false, ap_enabled: false, is_moderator: false, is_admin: false, @@ -1705,7 +1787,7 @@ def perform(:delete, %User{} = user) do delete_or_deactivate(user) end - def perform(:deactivate_async, user, status), do: deactivate(user, status) + def perform(:set_activation_async, user, status), do: set_activation(user, status) @spec external_users_query() :: Ecto.Query.t() def external_users_query do @@ -1985,6 +2067,15 @@ def local_nickname(nickname_or_mention) do |> hd() end + def full_nickname(%User{} = user) do + if String.contains?(user.nickname, "@") do + user.nickname + else + %{host: host} = URI.parse(user.ap_id) + user.nickname <> "@" <> host + end + end + def full_nickname(nickname_or_mention), do: String.trim_leading(nickname_or_mention, "@") @@ -1999,7 +2090,7 @@ def error_user(ap_id) do @spec all_superusers() :: [User.t()] def all_superusers do - User.Query.build(%{super_users: true, local: true, deactivated: false}) + User.Query.build(%{super_users: true, local: true, is_active: true}) |> Repo.all() end @@ -2040,7 +2131,7 @@ def list_inactive_users_query(inactivity_threshold \\ 7) do left_join: a in Pleroma.Activity, on: u.ap_id == a.actor, where: not is_nil(u.nickname), - where: u.deactivated != ^true, + where: u.is_active == ^true, where: u.id not in ^has_read_notifications, group_by: u.id, having: @@ -2081,22 +2172,10 @@ def touch_last_digest_emailed_at(user) do updated_user end - @spec toggle_confirmation(User.t()) :: {:ok, User.t()} | {:error, Changeset.t()} - def toggle_confirmation(%User{} = user) do + @spec set_confirmation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()} + def set_confirmation(%User{} = user, bool) do user - |> confirmation_changeset(need_confirmation: !user.confirmation_pending) - |> update_and_set_cache() - end - - @spec toggle_confirmation([User.t()]) :: [{:ok, User.t()} | {:error, Changeset.t()}] - def toggle_confirmation(users) do - Enum.map(users, &toggle_confirmation/1) - end - - @spec need_confirmation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()} - def need_confirmation(%User{} = user, bool) do - user - |> confirmation_changeset(need_confirmation: bool) + |> confirmation_changeset(set_confirmation: bool) |> update_and_set_cache() end @@ -2142,7 +2221,7 @@ def get_ap_ids_by_nicknames(nicknames) do defp put_password_hash( %Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset ) do - change(changeset, password_hash: Pbkdf2.hash_pwd_salt(password)) + change(changeset, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password)) end defp put_password_hash(changeset), do: changeset @@ -2173,9 +2252,9 @@ def change_email(user, email) do end # Internal function; public one is `deactivate/2` - defp set_activation_status(user, deactivated) do + defp set_activation_status(user, status) do user - |> cast(%{deactivated: deactivated}, [:deactivated]) + |> cast(%{is_active: status}, [:is_active]) |> update_and_set_cache() end @@ -2191,13 +2270,6 @@ def update_background(user, background) do |> update_and_set_cache() end - def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do - %{ - admin: is_admin, - moderator: is_moderator - } - end - def validate_fields(changeset, remote? \\ false) do limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields limit = Config.get([:instance, limit_name], 0) @@ -2264,68 +2336,57 @@ def mastodon_settings_update(user, settings) do end @spec confirmation_changeset(User.t(), keyword()) :: Changeset.t() - def confirmation_changeset(user, need_confirmation: need_confirmation?) do + def confirmation_changeset(user, set_confirmation: confirmed?) do params = - if need_confirmation? do + if confirmed? do %{ - confirmation_pending: true, - confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64() + is_confirmed: true, + confirmation_token: nil } else %{ - confirmation_pending: false, - confirmation_token: nil + is_confirmed: false, + confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64() } end - cast(user, params, [:confirmation_pending, :confirmation_token]) + cast(user, params, [:is_confirmed, :confirmation_token]) end @spec approval_changeset(User.t(), keyword()) :: Changeset.t() - def approval_changeset(user, need_approval: need_approval?) do - params = if need_approval?, do: %{approval_pending: true}, else: %{approval_pending: false} - cast(user, params, [:approval_pending]) + def approval_changeset(user, set_approval: approved?) do + cast(user, %{is_approved: approved?}, [:is_approved]) end - def add_pinnned_activity(user, %Pleroma.Activity{id: id}) do - if id not in user.pinned_activities do - max_pinned_statuses = Config.get([:instance, :max_pinned_statuses], 0) - params = %{pinned_activities: user.pinned_activities ++ [id]} - - # if pinned activity was scheduled for deletion, we remove job - if expiration = Pleroma.Workers.PurgeExpiredActivity.get_expiration(id) do - Oban.cancel_job(expiration.id) - end + @spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()} + def add_pinned_object_id(%User{} = user, object_id) do + if !user.pinned_objects[object_id] do + params = %{pinned_objects: Map.put(user.pinned_objects, object_id, NaiveDateTime.utc_now())} user - |> cast(params, [:pinned_activities]) - |> validate_length(:pinned_activities, - max: max_pinned_statuses, - message: "You have already pinned the maximum number of statuses" - ) + |> cast(params, [:pinned_objects]) + |> validate_change(:pinned_objects, fn :pinned_objects, pinned_objects -> + max_pinned_statuses = Config.get([:instance, :max_pinned_statuses], 0) + + if Enum.count(pinned_objects) <= max_pinned_statuses do + [] + else + [pinned_objects: "You have already pinned the maximum number of statuses"] + end + end) else change(user) end |> update_and_set_cache() end - def remove_pinnned_activity(user, %Pleroma.Activity{id: id, data: data}) do - params = %{pinned_activities: List.delete(user.pinned_activities, id)} - - # if pinned activity was scheduled for deletion, we reschedule it for deletion - if data["expires_at"] do - # MRF.ActivityExpirationPolicy used UTC timestamps for expires_at in original implementation - {:ok, expires_at} = - data["expires_at"] |> Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime.cast() - - Pleroma.Workers.PurgeExpiredActivity.enqueue(%{ - activity_id: id, - expires_at: expires_at - }) - end - + @spec remove_pinned_object_id(User.t(), String.t()) :: {:ok, t()} | {:error, term()} + def remove_pinned_object_id(%User{} = user, object_id) do user - |> cast(params, [:pinned_activities]) + |> cast( + %{pinned_objects: Map.delete(user.pinned_objects, object_id)}, + [:pinned_objects] + ) |> update_and_set_cache() end @@ -2365,7 +2426,7 @@ def unblock_domain(user, domain_blocked) do {:ok, UserRelationship.t()} | {:error, Ecto.Changeset.t()} defp add_to_block(%User{} = user, %User{} = blocked) do with {:ok, relationship} <- UserRelationship.create_block(user, blocked) do - Cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}") + @cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}") {:ok, relationship} end end @@ -2374,7 +2435,7 @@ defp add_to_block(%User{} = user, %User{} = blocked) do {:ok, UserRelationship.t()} | {:ok, nil} | {:error, Ecto.Changeset.t()} defp remove_from_block(%User{} = user, %User{} = blocked) do with {:ok, relationship} <- UserRelationship.delete_block(user, blocked) do - Cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}") + @cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}") {:ok, relationship} end end @@ -2412,4 +2473,19 @@ def sanitize_html(%User{} = user, filter) do def get_host(%User{ap_id: ap_id} = _user) do URI.parse(ap_id).host end + + def update_last_active_at(%__MODULE__{local: true} = user) do + user + |> cast(%{last_active_at: NaiveDateTime.utc_now()}, [:last_active_at]) + |> update_and_set_cache() + end + + def active_user_count(weeks \\ 4) do + active_after = Timex.shift(NaiveDateTime.utc_now(), weeks: -weeks) + + __MODULE__ + |> where([u], u.last_active_at >= ^active_after) + |> where([u], u.local == true) + |> Repo.aggregate(:count) + end end diff --git a/lib/pleroma/user/backup.ex b/lib/pleroma/user/backup.ex index a9041fd94..cba94248f 100644 --- a/lib/pleroma/user/backup.ex +++ b/lib/pleroma/user/backup.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.Backup do diff --git a/lib/pleroma/user/import.ex b/lib/pleroma/user/import.ex index 86b49d8ae..60cd18041 100644 --- a/lib/pleroma/user/import.ex +++ b/lib/pleroma/user/import.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.Import do diff --git a/lib/pleroma/user/notification_setting.ex b/lib/pleroma/user/notification_setting.ex index 7d9e8a000..a7cd61499 100644 --- a/lib/pleroma/user/notification_setting.ex +++ b/lib/pleroma/user/notification_setting.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.NotificationSetting do diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index 7ef2a1455..fa46545da 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.Query do @@ -137,8 +137,9 @@ defp compose_query({:local, _}, query), do: location_query(query, true) defp compose_query({:external, _}, query), do: location_query(query, false) defp compose_query({:active, _}, query) do - User.restrict_deactivated(query) - |> where([u], u.approval_pending == false) + where(query, [u], u.is_active == true) + |> where([u], u.is_approved == true) + |> where([u], u.is_confirmed == true) end defp compose_query({:legacy_active, _}, query) do @@ -147,23 +148,23 @@ defp compose_query({:legacy_active, _}, query) do end defp compose_query({:deactivated, false}, query) do - User.restrict_deactivated(query) + where(query, [u], u.is_active == true) end defp compose_query({:deactivated, true}, query) do - where(query, [u], u.deactivated == ^true) + where(query, [u], u.is_active == false) end defp compose_query({:confirmation_pending, bool}, query) do - where(query, [u], u.confirmation_pending == ^bool) + where(query, [u], u.is_confirmed != ^bool) end defp compose_query({:need_approval, _}, query) do - where(query, [u], u.approval_pending) + where(query, [u], u.is_approved == false) end defp compose_query({:unconfirmed, _}, query) do - where(query, [u], u.confirmation_pending) + where(query, [u], u.is_confirmed == false) end defp compose_query({:followers, %User{id: id}}, query) do diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex index f1761ef03..a4f6abca2 100644 --- a/lib/pleroma/user/search.ex +++ b/lib/pleroma/user/search.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.Search do diff --git a/lib/pleroma/user/welcome_chat_message.ex b/lib/pleroma/user/welcome_chat_message.ex index 3e7d1f424..0d6690e34 100644 --- a/lib/pleroma/user/welcome_chat_message.ex +++ b/lib/pleroma/user/welcome_chat_message.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.WelcomeChatMessage do diff --git a/lib/pleroma/user/welcome_email.ex b/lib/pleroma/user/welcome_email.ex index 5322000d4..295c1acc7 100644 --- a/lib/pleroma/user/welcome_email.ex +++ b/lib/pleroma/user/welcome_email.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.WelcomeEmail do diff --git a/lib/pleroma/user/welcome_message.ex b/lib/pleroma/user/welcome_message.ex index 86e1c0678..2cff05549 100644 --- a/lib/pleroma/user/welcome_message.ex +++ b/lib/pleroma/user/welcome_message.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.WelcomeMessage do diff --git a/lib/pleroma/user_invite_token.ex b/lib/pleroma/user_invite_token.ex index a08ba99f1..4cff1c515 100644 --- a/lib/pleroma/user_invite_token.ex +++ b/lib/pleroma/user_invite_token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.UserInviteToken do diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex index 6dfdd2860..a467e9b65 100644 --- a/lib/pleroma/user_relationship.ex +++ b/lib/pleroma/user_relationship.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.UserRelationship do diff --git a/lib/pleroma/utils.ex b/lib/pleroma/utils.ex index e95766223..a446d3ae6 100644 --- a/lib/pleroma/utils.ex +++ b/lib/pleroma/utils.ex @@ -1,8 +1,18 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Utils do + @posix_error_codes ~w( + eacces eagain ebadf ebadmsg ebusy edeadlk edeadlock edquot eexist efault + efbig eftype eintr einval eio eisdir eloop emfile emlink emultihop + enametoolong enfile enobufs enodev enolck enolink enoent enomem enospc + enosr enostr enosys enotblk enotdir enotsup enxio eopnotsupp eoverflow + eperm epipe erange erofs espipe esrch estale etxtbsy exdev + )a + + @repo_timeout Pleroma.Config.get([Pleroma.Repo, :timeout], 15_000) + def compile_dir(dir) when is_binary(dir) do dir |> File.ls!() @@ -22,7 +32,10 @@ def compile_dir(dir) when is_binary(dir) do """ @spec command_available?(String.t()) :: boolean() def command_available?(command) do - match?({_output, 0}, System.cmd("sh", ["-c", "command -v #{command}"])) + case :os.find_executable(String.to_charlist(command)) do + false -> false + _ -> true + end end @doc "creates the uniq temporary directory" @@ -44,4 +57,29 @@ def tmp_dir(prefix \\ "") do error -> error end end + + @spec posix_error_message(atom()) :: binary() + def posix_error_message(code) when code in @posix_error_codes do + error_message = Gettext.dgettext(Pleroma.Web.Gettext, "posix_errors", "#{code}") + "(POSIX error: #{error_message})" + end + + def posix_error_message(_), do: "" + + @doc """ + Returns [timeout: integer] suitable for passing as an option to Repo functions. + + This function detects if the execution was triggered from IEx shell, Mix task, or + ./bin/pleroma_ctl and sets the timeout to :infinity, else returns the default timeout value. + """ + @spec query_timeout() :: [timeout: integer] + def query_timeout do + {parent, _, _, _} = Process.info(self(), :current_stacktrace) |> elem(1) |> Enum.fetch!(2) + + cond do + parent |> to_string |> String.starts_with?("Elixir.Mix.Task") -> [timeout: :infinity] + parent == :erl_eval -> [timeout: :infinity] + true -> [timeout: @repo_timeout] + end + end end diff --git a/lib/pleroma/web.ex b/lib/pleroma/web.ex index 3ca20455d..8630f244b 100644 --- a/lib/pleroma/web.ex +++ b/lib/pleroma/web.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web do @@ -63,7 +63,8 @@ defp skip_plug(conn, plug_modules) do # Executed just before actual controller action, invokes before-action hooks (callbacks) defp action(conn, params) do - with %{halted: false} = conn <- maybe_drop_authentication_if_oauth_check_ignored(conn), + with %{halted: false} = conn <- + maybe_drop_authentication_if_oauth_check_ignored(conn), %{halted: false} = conn <- maybe_perform_public_or_authenticated_check(conn), %{halted: false} = conn <- maybe_perform_authenticated_check(conn), %{halted: false} = conn <- maybe_halt_on_missing_oauth_scopes_check(conn) do @@ -232,4 +233,16 @@ defmacro __using__(which) when is_atom(which) do def base_url do Pleroma.Web.Endpoint.url() end + + # TODO: Change to Phoenix.Router.routes/1 for Phoenix 1.6.0+ + def get_api_routes do + Pleroma.Web.Router.__routes__() + |> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end) + |> Enum.map(fn r -> + r.path + |> String.split("/", trim: true) + |> List.first() + end) + |> Enum.uniq() + end end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 1c91bc074..d0051d1cb 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ActivityPub do @@ -10,6 +10,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.Conversation alias Pleroma.Conversation.Participation alias Pleroma.Filter + alias Pleroma.Hashtag alias Pleroma.Maps alias Pleroma.Notification alias Pleroma.Object @@ -32,6 +33,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do require Logger require Pleroma.Constants + @behaviour Pleroma.Web.ActivityPub.ActivityPub.Persisting + @behaviour Pleroma.Web.ActivityPub.ActivityPub.Streaming + defp get_recipients(%{"type" => "Create"} = data) do to = Map.get(data, "to", []) cc = Map.get(data, "cc", []) @@ -53,7 +57,7 @@ defp check_actor_is_active(nil), do: true defp check_actor_is_active(actor) when is_binary(actor) do case User.get_cached_by_ap_id(actor) do - %User{deactivated: deactivated} -> not deactivated + %User{is_active: true} -> true _ -> false end end @@ -85,13 +89,14 @@ defp increase_replies_count_if_reply(%{ defp increase_replies_count_if_reply(_create_data), do: :noop @object_types ~w[ChatMessage Question Answer Audio Video Event Article] - @spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()} + @impl true def persist(%{"type" => type} = object, meta) when type in @object_types do with {:ok, object} <- Object.create(object) do {:ok, object, meta} end end + @impl true def persist(object, meta) do with local <- Keyword.fetch!(meta, :local), {recipients, _, _} <- get_recipients(object), @@ -221,6 +226,7 @@ def stream_out_participations(participations) do Streamer.stream("participation", participations) end + @impl true def stream_out_participations(%Object{data: %{"context" => context}}, user) do with %Conversation{} = conversation <- Conversation.get_for_ap_id(context) do conversation = Repo.preload(conversation, :participations) @@ -237,8 +243,10 @@ def stream_out_participations(%Object{data: %{"context" => context}}, user) do end end + @impl true def stream_out_participations(_, _), do: :noop + @impl true def stream_out(%Activity{data: %{"type" => data_type}} = activity) when data_type in ["Create", "Announce", "Delete"] do activity @@ -246,6 +254,7 @@ def stream_out(%Activity{data: %{"type" => data_type}} = activity) |> Streamer.stream(activity) end + @impl true def stream_out(_activity) do :noop end @@ -369,6 +378,7 @@ defp do_flag( :ok <- maybe_federate(stripped_activity) do User.all_superusers() + |> Enum.filter(fn user -> user.ap_id != actor end) |> Enum.filter(fn user -> not is_nil(user.email) end) |> Enum.each(fn superuser -> superuser @@ -456,6 +466,23 @@ def fetch_latest_direct_activity_id_for_context(context, opts \\ %{}) do |> Repo.one() end + defp fetch_paginated_optimized(query, opts, pagination) do + # Note: tag-filtering funcs may apply "ORDER BY objects.id DESC", + # and extra sorting on "activities.id DESC NULLS LAST" would worse the query plan + opts = Map.put(opts, :skip_extra_order, true) + + Pagination.fetch_paginated(query, opts, pagination) + end + + def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do + list_memberships = Pleroma.List.memberships(opts[:user]) + + fetch_activities_query(recipients ++ list_memberships, opts) + |> fetch_paginated_optimized(opts, pagination) + |> Enum.reverse() + |> maybe_update_cc(list_memberships, opts[:user]) + end + @spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()] def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do opts = Map.delete(opts, :user) @@ -463,7 +490,7 @@ def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do [Constants.as_public()] |> fetch_activities_query(opts) |> restrict_unlisted(opts) - |> Pagination.fetch_paginated(opts, pagination) + |> fetch_paginated_optimized(opts, pagination) end @spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()] @@ -583,13 +610,27 @@ def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do |> Enum.reverse() end - def fetch_user_activities(user, reading_user, params \\ %{}) do + def fetch_user_activities(user, reading_user, params \\ %{}) + + def fetch_user_activities(user, reading_user, %{total: true} = params) do + result = fetch_activities_for_user(user, reading_user, params) + + Keyword.put(result, :items, Enum.reverse(result[:items])) + end + + def fetch_user_activities(user, reading_user, params) do + user + |> fetch_activities_for_user(reading_user, params) + |> Enum.reverse() + end + + defp fetch_activities_for_user(user, reading_user, params) do params = params |> Map.put(:type, ["Create", "Announce"]) |> Map.put(:user, reading_user) |> Map.put(:actor_id, user.ap_id) - |> Map.put(:pinned_activity_ids, user.pinned_activities) + |> Map.put(:pinned_object_ids, Map.keys(user.pinned_objects)) params = if User.blocks?(reading_user, user) do @@ -600,16 +641,28 @@ def fetch_user_activities(user, reading_user, params \\ %{}) do |> Map.put(:muting_user, reading_user) end + pagination_type = Map.get(params, :pagination_type) || :keyset + %{ godmode: params[:godmode], reading_user: reading_user } |> user_activities_recipients() - |> fetch_activities(params) - |> Enum.reverse() + |> fetch_activities(params, pagination_type) + end + + def fetch_statuses(reading_user, %{total: true} = params) do + result = fetch_activities_for_reading_user(reading_user, params) + Keyword.put(result, :items, Enum.reverse(result[:items])) end def fetch_statuses(reading_user, params) do + reading_user + |> fetch_activities_for_reading_user(params) + |> Enum.reverse() + end + + defp fetch_activities_for_reading_user(reading_user, params) do params = Map.put(params, :type, ["Create", "Announce"]) %{ @@ -618,7 +671,6 @@ def fetch_statuses(reading_user, params) do } |> user_activities_recipients() |> fetch_activities(params, :offset) - |> Enum.reverse() end defp user_activities_recipients(%{godmode: true}), do: [] @@ -659,52 +711,144 @@ defp restrict_since(query, %{since_id: since_id}) do defp restrict_since(query, _), do: query - defp restrict_tag_reject(_query, %{tag_reject: _tag_reject, skip_preload: true}) do - raise "Can't use the child object without preloading!" + defp restrict_embedded_tag_all(_query, %{tag_all: _tag_all, skip_preload: true}) do + raise_on_missing_preload() end - defp restrict_tag_reject(query, %{tag_reject: [_ | _] = tag_reject}) do - from( - [_activity, object] in query, - where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject) - ) - end - - defp restrict_tag_reject(query, _), do: query - - defp restrict_tag_all(_query, %{tag_all: _tag_all, skip_preload: true}) do - raise "Can't use the child object without preloading!" - end - - defp restrict_tag_all(query, %{tag_all: [_ | _] = tag_all}) do + defp restrict_embedded_tag_all(query, %{tag_all: [_ | _] = tag_all}) do from( [_activity, object] in query, where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all) ) end - defp restrict_tag_all(query, _), do: query + defp restrict_embedded_tag_all(query, %{tag_all: tag}) when is_binary(tag) do + restrict_embedded_tag_any(query, %{tag: tag}) + end - defp restrict_tag(_query, %{tag: _tag, skip_preload: true}) do + defp restrict_embedded_tag_all(query, _), do: query + + defp restrict_embedded_tag_any(_query, %{tag: _tag, skip_preload: true}) do + raise_on_missing_preload() + end + + defp restrict_embedded_tag_any(query, %{tag: [_ | _] = tag_any}) do + from( + [_activity, object] in query, + where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag_any) + ) + end + + defp restrict_embedded_tag_any(query, %{tag: tag}) when is_binary(tag) do + restrict_embedded_tag_any(query, %{tag: [tag]}) + end + + defp restrict_embedded_tag_any(query, _), do: query + + defp restrict_embedded_tag_reject_any(_query, %{tag_reject: _tag_reject, skip_preload: true}) do + raise_on_missing_preload() + end + + defp restrict_embedded_tag_reject_any(query, %{tag_reject: [_ | _] = tag_reject}) do + from( + [_activity, object] in query, + where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject) + ) + end + + defp restrict_embedded_tag_reject_any(query, %{tag_reject: tag_reject}) + when is_binary(tag_reject) do + restrict_embedded_tag_reject_any(query, %{tag_reject: [tag_reject]}) + end + + defp restrict_embedded_tag_reject_any(query, _), do: query + + defp object_ids_query_for_tags(tags) do + from(hto in "hashtags_objects") + |> join(:inner, [hto], ht in Pleroma.Hashtag, on: hto.hashtag_id == ht.id) + |> where([hto, ht], ht.name in ^tags) + |> select([hto], hto.object_id) + |> distinct([hto], true) + end + + defp restrict_hashtag_all(_query, %{tag_all: _tag, skip_preload: true}) do + raise_on_missing_preload() + end + + defp restrict_hashtag_all(query, %{tag_all: [single_tag]}) do + restrict_hashtag_any(query, %{tag: single_tag}) + end + + defp restrict_hashtag_all(query, %{tag_all: [_ | _] = tags}) do + from( + [_activity, object] in query, + where: + fragment( + """ + (SELECT array_agg(hashtags.name) FROM hashtags JOIN hashtags_objects + ON hashtags_objects.hashtag_id = hashtags.id WHERE hashtags.name = ANY(?) + AND hashtags_objects.object_id = ?) @> ? + """, + ^tags, + object.id, + ^tags + ) + ) + end + + defp restrict_hashtag_all(query, %{tag_all: tag}) when is_binary(tag) do + restrict_hashtag_all(query, %{tag_all: [tag]}) + end + + defp restrict_hashtag_all(query, _), do: query + + defp restrict_hashtag_any(_query, %{tag: _tag, skip_preload: true}) do + raise_on_missing_preload() + end + + defp restrict_hashtag_any(query, %{tag: [_ | _] = tags}) do + hashtag_ids = + from(ht in Hashtag, where: ht.name in ^tags, select: ht.id) + |> Repo.all() + + # Note: NO extra ordering should be done on "activities.id desc nulls last" for optimal plan + from( + [_activity, object] in query, + join: hto in "hashtags_objects", + on: hto.object_id == object.id, + where: hto.hashtag_id in ^hashtag_ids, + distinct: [desc: object.id], + order_by: [desc: object.id] + ) + end + + defp restrict_hashtag_any(query, %{tag: tag}) when is_binary(tag) do + restrict_hashtag_any(query, %{tag: [tag]}) + end + + defp restrict_hashtag_any(query, _), do: query + + defp restrict_hashtag_reject_any(_query, %{tag_reject: _tag_reject, skip_preload: true}) do + raise_on_missing_preload() + end + + defp restrict_hashtag_reject_any(query, %{tag_reject: [_ | _] = tags_reject}) do + from( + [_activity, object] in query, + where: object.id not in subquery(object_ids_query_for_tags(tags_reject)) + ) + end + + defp restrict_hashtag_reject_any(query, %{tag_reject: tag_reject}) when is_binary(tag_reject) do + restrict_hashtag_reject_any(query, %{tag_reject: [tag_reject]}) + end + + defp restrict_hashtag_reject_any(query, _), do: query + + defp raise_on_missing_preload do raise "Can't use the child object without preloading!" end - defp restrict_tag(query, %{tag: tag}) when is_list(tag) do - from( - [_activity, object] in query, - where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag) - ) - end - - defp restrict_tag(query, %{tag: tag}) when is_binary(tag) do - from( - [_activity, object] in query, - where: fragment("(?)->'tag' \\? (?)", object.data, ^tag) - ) - end - - defp restrict_tag(query, _), do: query - defp restrict_recipients(query, [], _user), do: query defp restrict_recipients(query, recipients, nil) do @@ -725,6 +869,12 @@ defp restrict_local(query, %{local_only: true}) do defp restrict_local(query, _), do: query + defp restrict_remote(query, %{remote: true}) do + from(activity in query, where: activity.local == false) + end + + defp restrict_remote(query, _), do: query + defp restrict_actor(query, %{actor_id: actor_id}) do from(activity in query, where: activity.actor == ^actor_id) end @@ -925,8 +1075,18 @@ defp restrict_unlisted(query, %{restrict_unlisted: true}) do defp restrict_unlisted(query, _), do: query - defp restrict_pinned(query, %{pinned: true, pinned_activity_ids: ids}) do - from(activity in query, where: activity.id in ^ids) + defp restrict_pinned(query, %{pinned: true, pinned_object_ids: ids}) do + from( + [activity, object: o] in query, + where: + fragment( + "(?)->>'type' = 'Create' and coalesce((?)->'object'->>'id', (?)->>'object') = any (?)", + activity.data, + activity.data, + activity.data, + ^ids + ) + ) end defp restrict_pinned(query, _), do: query @@ -1058,6 +1218,26 @@ defp maybe_order(query, %{order: :asc}) do defp maybe_order(query, _), do: query + defp normalize_fetch_activities_query_opts(opts) do + Enum.reduce([:tag, :tag_all, :tag_reject], opts, fn key, opts -> + case opts[key] do + value when is_bitstring(value) -> + Map.put(opts, key, Hashtag.normalize_name(value)) + + value when is_list(value) -> + normalized_value = + value + |> Enum.map(&Hashtag.normalize_name/1) + |> Enum.uniq() + + Map.put(opts, key, normalized_value) + + _ -> + opts + end + end) + end + defp fetch_activities_query_ap_ids_ops(opts) do source_user = opts[:muting_user] ap_id_relationships = if source_user, do: [:mute, :reblog_mute], else: [] @@ -1081,6 +1261,8 @@ defp fetch_activities_query_ap_ids_ops(opts) do end def fetch_activities_query(recipients, opts \\ %{}) do + opts = normalize_fetch_activities_query_opts(opts) + {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts} = fetch_activities_query_ap_ids_ops(opts) @@ -1088,49 +1270,51 @@ def fetch_activities_query(recipients, opts \\ %{}) do skip_thread_containment: Config.get([:instance, :skip_thread_containment]) } - Activity - |> maybe_preload_objects(opts) - |> maybe_preload_bookmarks(opts) - |> maybe_preload_report_notes(opts) - |> maybe_set_thread_muted_field(opts) - |> maybe_order(opts) - |> restrict_recipients(recipients, opts[:user]) - |> restrict_replies(opts) - |> restrict_tag(opts) - |> restrict_tag_reject(opts) - |> restrict_tag_all(opts) - |> restrict_since(opts) - |> restrict_local(opts) - |> restrict_actor(opts) - |> restrict_type(opts) - |> restrict_state(opts) - |> restrict_favorited_by(opts) - |> restrict_blocked(restrict_blocked_opts) - |> restrict_muted(restrict_muted_opts) - |> restrict_filtered(opts) - |> restrict_media(opts) - |> restrict_visibility(opts) - |> restrict_thread_visibility(opts, config) - |> restrict_reblogs(opts) - |> restrict_pinned(opts) - |> restrict_muted_reblogs(restrict_muted_reblogs_opts) - |> restrict_instance(opts) - |> restrict_announce_object_actor(opts) - |> restrict_filtered(opts) - |> Activity.restrict_deactivated_users() - |> exclude_poll_votes(opts) - |> exclude_chat_messages(opts) - |> exclude_invisible_actors(opts) - |> exclude_visibility(opts) - end + query = + Activity + |> maybe_preload_objects(opts) + |> maybe_preload_bookmarks(opts) + |> maybe_preload_report_notes(opts) + |> maybe_set_thread_muted_field(opts) + |> maybe_order(opts) + |> restrict_recipients(recipients, opts[:user]) + |> restrict_replies(opts) + |> restrict_since(opts) + |> restrict_local(opts) + |> restrict_remote(opts) + |> restrict_actor(opts) + |> restrict_type(opts) + |> restrict_state(opts) + |> restrict_favorited_by(opts) + |> restrict_blocked(restrict_blocked_opts) + |> restrict_muted(restrict_muted_opts) + |> restrict_filtered(opts) + |> restrict_media(opts) + |> restrict_visibility(opts) + |> restrict_thread_visibility(opts, config) + |> restrict_reblogs(opts) + |> restrict_pinned(opts) + |> restrict_muted_reblogs(restrict_muted_reblogs_opts) + |> restrict_instance(opts) + |> restrict_announce_object_actor(opts) + |> restrict_filtered(opts) + |> Activity.restrict_deactivated_users() + |> exclude_poll_votes(opts) + |> exclude_chat_messages(opts) + |> exclude_invisible_actors(opts) + |> exclude_visibility(opts) - def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do - list_memberships = Pleroma.List.memberships(opts[:user]) - - fetch_activities_query(recipients ++ list_memberships, opts) - |> Pagination.fetch_paginated(opts, pagination) - |> Enum.reverse() - |> maybe_update_cc(list_memberships, opts[:user]) + if Config.feature_enabled?(:improved_hashtag_timeline) do + query + |> restrict_hashtag_any(opts) + |> restrict_hashtag_all(opts) + |> restrict_hashtag_reject_any(opts) + else + query + |> restrict_embedded_tag_any(opts) + |> restrict_embedded_tag_all(opts) + |> restrict_embedded_tag_reject_any(opts) + end end @doc """ @@ -1209,21 +1393,17 @@ defp get_actor_url(url) when is_list(url) do defp get_actor_url(_url), do: nil + defp normalize_image(%{"url" => url}) do + %{ + "type" => "Image", + "url" => [%{"href" => url}] + } + end + + defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image() + defp normalize_image(_), do: nil + defp object_to_user_data(data) do - avatar = - data["icon"]["url"] && - %{ - "type" => "Image", - "url" => [%{"href" => data["icon"]["url"]}] - } - - banner = - data["image"]["url"] && - %{ - "type" => "Image", - "url" => [%{"href" => data["image"]["url"]}] - } - fields = data |> Map.get("attachment", []) @@ -1249,6 +1429,9 @@ defp object_to_user_data(data) do invisible = data["invisible"] || false actor_type = data["type"] || "Person" + featured_address = data["featured"] + {:ok, pinned_objects} = fetch_and_prepare_featured_from_ap_id(featured_address) + public_key = if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do data["publicKey"]["publicKeyPem"] @@ -1267,23 +1450,25 @@ defp object_to_user_data(data) do ap_id: data["id"], uri: get_actor_url(data["url"]), ap_enabled: true, - banner: banner, + banner: normalize_image(data["image"]), fields: fields, emoji: emojis, is_locked: is_locked, is_discoverable: is_discoverable, invisible: invisible, - avatar: avatar, + avatar: normalize_image(data["icon"]), name: data["name"], follower_address: data["followers"], following_address: data["following"], + featured_address: featured_address, bio: data["summary"] || "", actor_type: actor_type, also_known_as: Map.get(data, "alsoKnownAs", []), public_key: public_key, inbox: data["inbox"], shared_inbox: shared_inbox, - accepts_chat_messages: accepts_chat_messages + accepts_chat_messages: accepts_chat_messages, + pinned_objects: pinned_objects } # nickname can be nil because of virtual actors @@ -1421,6 +1606,41 @@ def maybe_handle_clashing_nickname(data) do end end + def pin_data_from_featured_collection(%{ + "type" => type, + "orderedItems" => objects + }) + when type in ["OrderedCollection", "Collection"] do + Map.new(objects, fn %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()} end) + end + + def fetch_and_prepare_featured_from_ap_id(nil) do + {:ok, %{}} + end + + def fetch_and_prepare_featured_from_ap_id(ap_id) do + with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do + {:ok, pin_data_from_featured_collection(data)} + else + e -> + Logger.error("Could not decode featured collection at fetch #{ap_id}, #{inspect(e)}") + {:ok, %{}} + end + end + + def pinned_fetch_task(nil), do: nil + + def pinned_fetch_task(%{pinned_objects: pins}) do + if Enum.all?(pins, fn {ap_id, _} -> + Object.get_cached_by_ap_id(ap_id) || + match?({:ok, _object}, Fetcher.fetch_object_from_id(ap_id)) + end) do + :ok + else + :error + end + end + def make_user_from_ap_id(ap_id) do user = User.get_cached_by_ap_id(ap_id) @@ -1428,6 +1648,8 @@ def make_user_from_ap_id(ap_id) do Transmogrifier.upgrade_user_from_ap_id(ap_id) else with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do + {:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end) + if user do user |> User.remote_user_changeset(data) diff --git a/lib/pleroma/web/activity_pub/activity_pub/persisting.ex b/lib/pleroma/web/activity_pub/activity_pub/persisting.ex new file mode 100644 index 000000000..5ec8b7bab --- /dev/null +++ b/lib/pleroma/web/activity_pub/activity_pub/persisting.ex @@ -0,0 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ActivityPub.Persisting do + @callback persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()} +end diff --git a/lib/pleroma/web/activity_pub/activity_pub/streaming.ex b/lib/pleroma/web/activity_pub/activity_pub/streaming.ex new file mode 100644 index 000000000..983168bff --- /dev/null +++ b/lib/pleroma/web/activity_pub/activity_pub/streaming.ex @@ -0,0 +1,12 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ActivityPub.Streaming do + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.User + + @callback stream_out(Activity.t()) :: any() + @callback stream_out_participations(Object.t(), User.t()) :: any() +end diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 7e5647f8f..5aa3b281a 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ActivityPubController do @@ -79,11 +79,11 @@ def user(conn, %{"nickname" => nickname}) do end end - def object(conn, _) do + def object(%{assigns: assigns} = conn, _) do with ap_id <- Endpoint.url() <> conn.request_path, %Object{} = object <- Object.get_cached_by_ap_id(ap_id), - {_, true} <- {:public?, Visibility.is_public?(object)}, - {_, false} <- {:local?, Visibility.is_local_public?(object)} do + user <- Map.get(assigns, :user, nil), + {_, true} <- {:visible?, Visibility.visible_for_user?(object, user)} do conn |> assign(:tracking_fun_data, object.id) |> set_cache_ttl_for(object) @@ -91,11 +91,8 @@ def object(conn, _) do |> put_view(ObjectView) |> render("object.json", object: object) else - {:public?, false} -> - {:error, :not_found} - - {:local?, true} -> - {:error, :not_found} + {:visible?, false} -> {:error, :not_found} + nil -> {:error, :not_found} end end @@ -109,11 +106,12 @@ def track_object_fetch(conn, object_id) do conn end - def activity(conn, _params) do + def activity(%{assigns: assigns} = conn, _) do with ap_id <- Endpoint.url() <> conn.request_path, %Activity{} = activity <- Activity.normalize(ap_id), - {_, true} <- {:public?, Visibility.is_public?(activity)}, - {_, false} <- {:local?, Visibility.is_local_public?(activity)} do + {_, true} <- {:local?, activity.local}, + user <- Map.get(assigns, :user, nil), + {_, true} <- {:visible?, Visibility.visible_for_user?(activity, user)} do conn |> maybe_set_tracking_data(activity) |> set_cache_ttl_for(activity) @@ -121,14 +119,14 @@ def activity(conn, _params) do |> put_view(ObjectView) |> render("object.json", object: activity) else - {:public?, false} -> {:error, :not_found} - {:local?, true} -> {:error, :not_found} + {:visible?, false} -> {:error, :not_found} + {:local?, false} -> {:error, :not_found} nil -> {:error, :not_found} end end defp maybe_set_tracking_data(conn, %Activity{data: %{"type" => "Create"}} = activity) do - object_id = Object.normalize(activity).id + object_id = Object.normalize(activity, fetch: false).id assign(conn, :tracking_fun_data, object_id) end @@ -434,7 +432,7 @@ defp handle_user_activity( end defp handle_user_activity(%User{} = user, %{"type" => "Delete"} = params) do - with %Object{} = object <- Object.normalize(params["object"]), + with %Object{} = object <- Object.normalize(params["object"], fetch: false), true <- user.is_moderator || user.ap_id == object.data["actor"], {:ok, delete_data, _} <- Builder.delete(user, object.data["id"]), {:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do @@ -445,7 +443,7 @@ defp handle_user_activity(%User{} = user, %{"type" => "Delete"} = params) do end defp handle_user_activity(%User{} = user, %{"type" => "Like"} = params) do - with %Object{} = object <- Object.normalize(params["object"]), + with %Object{} = object <- Object.normalize(params["object"], fetch: false), {_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)}, {_, {:ok, %Activity{} = activity, _meta}} <- {:common_pipeline, @@ -545,4 +543,12 @@ def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = |> json(object.data) end end + + def pinned(conn, %{"nickname" => nickname}) do + with %User{} = user <- User.get_cached_by_nickname(nickname) do + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(UserView.render("featured.json", %{user: user})) + end + end end diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index e99f6fd83..91a45836f 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Builder do @@ -80,7 +80,7 @@ def undo(actor, object) do @spec delete(User.t(), String.t()) :: {:ok, map(), keyword()} def delete(actor, object_id) do - object = Object.normalize(object_id, false) + object = Object.normalize(object_id, fetch: false) user = !object && User.get_cached_by_ap_id(object_id) @@ -273,4 +273,36 @@ defp object_action(actor, object) do "context" => object.data["context"] }, []} end + + @spec pin(User.t(), Object.t()) :: {:ok, map(), keyword()} + def pin(%User{} = user, object) do + {:ok, + %{ + "id" => Utils.generate_activity_id(), + "target" => pinned_url(user.nickname), + "object" => object.data["id"], + "actor" => user.ap_id, + "type" => "Add", + "to" => [Pleroma.Constants.as_public()], + "cc" => [user.follower_address] + }, []} + end + + @spec unpin(User.t(), Object.t()) :: {:ok, map, keyword()} + def unpin(%User{} = user, object) do + {:ok, + %{ + "id" => Utils.generate_activity_id(), + "target" => pinned_url(user.nickname), + "object" => object.data["id"], + "actor" => user.ap_id, + "type" => "Remove", + "to" => [Pleroma.Constants.as_public()], + "cc" => [user.follower_address] + }, []} + end + + defp pinned_url(nickname) when is_binary(nickname) do + Pleroma.Web.Router.Helpers.activity_pub_url(Pleroma.Web.Endpoint, :pinned, nickname) + end end diff --git a/lib/pleroma/web/activity_pub/internal_fetch_actor.ex b/lib/pleroma/web/activity_pub/internal_fetch_actor.ex index c80272b8f..ca76071e5 100644 --- a/lib/pleroma/web/activity_pub/internal_fetch_actor.ex +++ b/lib/pleroma/web/activity_pub/internal_fetch_actor.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.InternalFetchActor do diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex index 6e73b2f22..f2fec3ff6 100644 --- a/lib/pleroma/web/activity_pub/mrf.ex +++ b/lib/pleroma/web/activity_pub/mrf.ex @@ -1,10 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF do require Logger + @behaviour Pleroma.Web.ActivityPub.MRF.PipelineFiltering + @mrf_config_descriptions [ %{ group: :pleroma, @@ -70,6 +72,7 @@ def filter(policies, %{} = message) do def filter(%{} = object), do: get_policies() |> filter(object) + @impl true def pipeline_filter(%{} = message, meta) do object = meta[:object_data] ap_id = message["object"] @@ -89,7 +92,9 @@ def pipeline_filter(%{} = message, meta) do end def get_policies do - Pleroma.Config.get([:mrf, :policies], []) |> get_policies() + Pleroma.Config.get([:mrf, :policies], []) + |> get_policies() + |> Enum.concat([Pleroma.Web.ActivityPub.MRF.HashtagPolicy]) end defp get_policies(policy) when is_atom(policy), do: [policy] diff --git a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex index 655a2ced0..fc347236e 100644 --- a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex index b96388489..b8bfdc3ce 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex index b22464111..40b19c3ab 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex index 5ab9844ff..378175205 100644 --- a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex index 3bf70b894..2d3a10889 100644 --- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex +++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do @@ -31,7 +31,7 @@ def filter(%{"type" => "Create", "object" => child_object} = object) when is_map(child_object) do child = child_object["inReplyTo"] - |> Object.normalize(child_object["inReplyTo"]) + |> Object.normalize(fetch: false) |> filter_by_summary(child_object) object = Map.put(object, "object", child) diff --git a/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex new file mode 100644 index 000000000..7307c9c14 --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex @@ -0,0 +1,59 @@ +defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do + @behaviour Pleroma.Web.ActivityPub.MRF + alias Pleroma.Config + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + require Logger + + @impl true + def filter(message) do + with follower_nickname <- Config.get([:mrf_follow_bot, :follower_nickname]), + %User{actor_type: "Service"} = follower <- + User.get_cached_by_nickname(follower_nickname), + %{"type" => "Create", "object" => %{"type" => "Note"}} <- message do + try_follow(follower, message) + else + nil -> + Logger.warn( + "#{__MODULE__} skipped because of missing `:mrf_follow_bot, :follower_nickname` configuration, the :follower_nickname + account does not exist, or the account is not correctly configured as a bot." + ) + + {:ok, message} + + _ -> + {:ok, message} + end + end + + defp try_follow(follower, message) do + to = Map.get(message, "to", []) + cc = Map.get(message, "cc", []) + actor = [message["actor"]] + + Enum.concat([to, cc, actor]) + |> List.flatten() + |> Enum.uniq() + |> User.get_all_by_ap_id() + |> Enum.each(fn user -> + with false <- user.local, + false <- User.following?(follower, user), + false <- User.locked?(user), + false <- (user.bio || "") |> String.downcase() |> String.contains?("nobot") do + Logger.debug( + "#{__MODULE__}: Follow request from #{follower.nickname} to #{user.nickname}" + ) + + CommonAPI.follow(follower, user) + end + end) + + {:ok, message} + end + + @impl true + def describe do + {:ok, %{}} + end +end diff --git a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex index ea9c3d3f5..51dbb1ad4 100644 --- a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex new file mode 100644 index 000000000..def0c437c --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex @@ -0,0 +1,116 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do + require Pleroma.Constants + + alias Pleroma.Config + alias Pleroma.Object + + @moduledoc """ + Reject, TWKN-remove or Set-Sensitive messsages with specific hashtags (without the leading #) + + Note: This MRF Policy is always enabled, if you want to disable it you have to set empty lists. + """ + + @behaviour Pleroma.Web.ActivityPub.MRF + + defp check_reject(message, hashtags) do + if Enum.any?(Config.get([:mrf_hashtag, :reject]), fn match -> match in hashtags end) do + {:reject, "[HashtagPolicy] Matches with rejected keyword"} + else + {:ok, message} + end + end + + defp check_ftl_removal(%{"to" => to} = message, hashtags) do + if Pleroma.Constants.as_public() in to and + Enum.any?(Config.get([:mrf_hashtag, :federated_timeline_removal]), fn match -> + match in hashtags + end) do + to = List.delete(to, Pleroma.Constants.as_public()) + cc = [Pleroma.Constants.as_public() | message["cc"] || []] + + message = + message + |> Map.put("to", to) + |> Map.put("cc", cc) + |> Kernel.put_in(["object", "to"], to) + |> Kernel.put_in(["object", "cc"], cc) + + {:ok, message} + else + {:ok, message} + end + end + + defp check_ftl_removal(message, _hashtags), do: {:ok, message} + + defp check_sensitive(message, hashtags) do + if Enum.any?(Config.get([:mrf_hashtag, :sensitive]), fn match -> match in hashtags end) do + {:ok, Kernel.put_in(message, ["object", "sensitive"], true)} + else + {:ok, message} + end + end + + @impl true + def filter(%{"type" => "Create", "object" => object} = message) do + hashtags = Object.hashtags(%Object{data: object}) + + if hashtags != [] do + with {:ok, message} <- check_reject(message, hashtags), + {:ok, message} <- check_ftl_removal(message, hashtags), + {:ok, message} <- check_sensitive(message, hashtags) do + {:ok, message} + end + else + {:ok, message} + end + end + + @impl true + def filter(message), do: {:ok, message} + + @impl true + def describe do + mrf_hashtag = + Config.get(:mrf_hashtag) + |> Enum.into(%{}) + + {:ok, %{mrf_hashtag: mrf_hashtag}} + end + + @impl true + def config_description do + %{ + key: :mrf_hashtag, + related_policy: "Pleroma.Web.ActivityPub.MRF.HashtagPolicy", + label: "MRF Hashtag", + description: @moduledoc, + children: [ + %{ + key: :reject, + type: {:list, :string}, + description: "A list of hashtags which result in message being rejected.", + suggestions: ["foo"] + }, + %{ + key: :federated_timeline_removal, + type: {:list, :string}, + description: + "A list of hashtags which result in message being removed from federated timelines (a.k.a unlisted).", + suggestions: ["foo"] + }, + %{ + key: :sensitive, + type: {:list, :string}, + description: + "A list of hashtags which result in message being set as sensitive (a.k.a NSFW/R-18)", + suggestions: ["nsfw", "r18"] + } + ] + } + end +end diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex index 3fd5c1e0a..768a669f3 100644 --- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex index ded0fe7f2..f91b51bcf 100644 --- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex index 816cc89bf..8dbf44071 100644 --- a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do @@ -27,7 +27,7 @@ defp prefetch(url) do if Pleroma.Config.get(:env) == :test do fetch(prefetch_url) else - ConcurrentLimiter.limit(MediaProxy, fn -> + ConcurrentLimiter.limit(__MODULE__, fn -> Task.start(fn -> fetch(prefetch_url) end) end) end diff --git a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex index 9c096712a..877277d4f 100644 --- a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex new file mode 100644 index 000000000..32bb1b645 --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex @@ -0,0 +1,61 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do + @moduledoc "Filter local activities which have no content" + @behaviour Pleroma.Web.ActivityPub.MRF + + alias Pleroma.Web + + @impl true + def filter(%{"actor" => actor} = object) do + with true <- is_local?(actor), + true <- is_note?(object), + false <- has_attachment?(object), + true <- only_mentions?(object) do + {:reject, "[NoEmptyPolicy]"} + else + _ -> + {:ok, object} + end + end + + def filter(object), do: {:ok, object} + + defp is_local?(actor) do + if actor |> String.starts_with?("#{Web.base_url()}") do + true + else + false + end + end + + defp has_attachment?(%{ + "type" => "Create", + "object" => %{"type" => "Note", "attachment" => attachments} + }) + when length(attachments) > 0, + do: true + + defp has_attachment?(_), do: false + + defp only_mentions?(%{"type" => "Create", "object" => %{"type" => "Note", "source" => source}}) do + non_mentions = + source |> String.split() |> Enum.filter(&(not String.starts_with?(&1, "@"))) |> length + + if non_mentions > 0 do + false + else + true + end + end + + defp only_mentions?(_), do: false + + defp is_note?(%{"type" => "Create", "object" => %{"type" => "Note"}}), do: true + defp is_note?(_), do: false + + @impl true + def describe, do: {:ok, %{}} +end diff --git a/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex index cc2ac9d08..2ebc0674d 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex index fc3475048..b658d7d41 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex index e00575c2a..2ad3fde0b 100644 --- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex +++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do diff --git a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex index eb0481f20..aac24c0ec 100644 --- a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex b/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex new file mode 100644 index 000000000..be95e38ec --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex @@ -0,0 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.PipelineFiltering do + @callback pipeline_filter(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} +end diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex index cd7665e31..47a43c6a2 100644 --- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex +++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 6cd91826d..62024c58c 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do @@ -64,20 +64,16 @@ defp check_media_nsfw( %{host: actor_host} = _actor_info, %{ "type" => "Create", - "object" => child_object + "object" => %{} = _child_object } = object - ) - when is_map(child_object) do + ) do media_nsfw = Config.get([:mrf_simple, :media_nsfw]) |> MRF.subdomains_regex() object = if MRF.subdomain_match?(media_nsfw, actor_host) do - tags = (child_object["tag"] || []) ++ ["nsfw"] - child_object = Map.put(child_object, "tag", tags) - child_object = Map.put(child_object, "sensitive", true) - Map.put(object, "object", child_object) + Kernel.put_in(object, ["object", "sensitive"], true) else object end diff --git a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex index 2858af9eb..4c5e33619 100644 --- a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do @@ -10,73 +10,75 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do @moduledoc "Detect new emojis by their shortcode and steals them" @behaviour Pleroma.Web.ActivityPub.MRF - defp remote_host?(host), do: host != Config.get([Pleroma.Web.Endpoint, :url, :host]) - defp accept_host?(host), do: host in Config.get([:mrf_steal_emoji, :hosts], []) - defp steal_emoji({shortcode, url}) do + defp steal_emoji({shortcode, url}, emoji_dir_path) do url = Pleroma.Web.MediaProxy.url(url) - {:ok, response} = Pleroma.HTTP.get(url) - size_limit = Config.get([:mrf_steal_emoji, :size_limit], 50_000) - if byte_size(response.body) <= size_limit do - emoji_dir_path = - Config.get( - [:mrf_steal_emoji, :path], - Path.join(Config.get([:instance, :static_dir]), "emoji/stolen") + with {:ok, %{status: status} = response} when status in 200..299 <- Pleroma.HTTP.get(url) do + size_limit = Config.get([:mrf_steal_emoji, :size_limit], 50_000) + + if byte_size(response.body) <= size_limit do + extension = + url + |> URI.parse() + |> Map.get(:path) + |> Path.basename() + |> Path.extname() + + file_path = Path.join(emoji_dir_path, shortcode <> (extension || ".png")) + + case File.write(file_path, response.body) do + :ok -> + shortcode + + e -> + Logger.warn("MRF.StealEmojiPolicy: Failed to write to #{file_path}: #{inspect(e)}") + nil + end + else + Logger.debug( + "MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{ + size_limit + } B)" ) - extension = - url - |> URI.parse() - |> Map.get(:path) - |> Path.basename() - |> Path.extname() - - file_path = Path.join([emoji_dir_path, shortcode <> (extension || ".png")]) - - try do - :ok = File.write(file_path, response.body) - - shortcode - rescue - e -> - Logger.warn("MRF.StealEmojiPolicy: Failed to write to #{file_path}: #{inspect(e)}") - nil + nil end else - Logger.debug( - "MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{ - size_limit - } B)" - ) - - nil + e -> + Logger.warn("MRF.StealEmojiPolicy: Failed to fetch #{url}: #{inspect(e)}") + nil end - rescue - e -> - Logger.warn("MRF.StealEmojiPolicy: Failed to fetch #{url}: #{inspect(e)}") - nil end @impl true def filter(%{"object" => %{"emoji" => foreign_emojis, "actor" => actor}} = message) do host = URI.parse(actor).host - if remote_host?(host) and accept_host?(host) do + if host != Pleroma.Web.Endpoint.host() and accept_host?(host) do installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) + emoji_dir_path = + Config.get( + [:mrf_steal_emoji, :path], + Path.join(Config.get([:instance, :static_dir]), "emoji/stolen") + ) + + File.mkdir_p(emoji_dir_path) + new_emojis = foreign_emojis - |> Enum.filter(fn {shortcode, _url} -> shortcode not in installed_emoji end) + |> Enum.reject(fn {shortcode, _url} -> shortcode in installed_emoji end) |> Enum.filter(fn {shortcode, _url} -> reject_emoji? = - Config.get([:mrf_steal_emoji, :rejected_shortcodes], []) + [:mrf_steal_emoji, :rejected_shortcodes] + |> Config.get([]) |> Enum.find(false, fn regex -> String.match?(shortcode, regex) end) !reject_emoji? end) - |> Enum.map(&steal_emoji(&1)) + |> Enum.map(&steal_emoji(&1, emoji_dir_path)) |> Enum.filter(& &1) if !Enum.empty?(new_emojis) do diff --git a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex index 2ec45260a..86965d47b 100644 --- a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex index febabda08..528093ac0 100644 --- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do @@ -28,20 +28,11 @@ defp process_tag( "mrf_tag:media-force-nsfw", %{ "type" => "Create", - "object" => %{"attachment" => child_attachment} = object + "object" => %{"attachment" => child_attachment} } = message ) when length(child_attachment) > 0 do - tags = (object["tag"] || []) ++ ["nsfw"] - - object = - object - |> Map.put("tag", tags) - |> Map.put("sensitive", true) - - message = Map.put(message, "object", object) - - {:ok, message} + {:ok, Kernel.put_in(message, ["object", "sensitive"], true)} end defp process_tag( diff --git a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex index e9d0d0503..65b371bf3 100644 --- a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do diff --git a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex index f325cb680..ce559a239 100644 --- a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index bd0a2a8dc..1dce33f1a 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidator do @@ -9,12 +9,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do the system. """ + @behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating + alias Pleroma.Activity alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object alias Pleroma.Object.Containment alias Pleroma.User alias Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator @@ -32,40 +35,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator alias Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator - @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} + @impl true def validate(object, meta) - def validate(%{"type" => type} = object, meta) - when type in ~w[Accept Reject] do - with {:ok, object} <- - object - |> AcceptRejectValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "Event"} = object, meta) do - with {:ok, object} <- - object - |> EventValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "Follow"} = object, meta) do - with {:ok, object} <- - object - |> FollowValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - def validate(%{"type" => "Block"} = block_activity, meta) do with {:ok, block_activity} <- block_activity @@ -85,16 +57,6 @@ def validate(%{"type" => "Block"} = block_activity, meta) do end end - def validate(%{"type" => "Update"} = update_activity, meta) do - with {:ok, update_activity} <- - update_activity - |> UpdateValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - update_activity = stringify_keys(update_activity) - {:ok, update_activity, meta} - end - end - def validate(%{"type" => "Undo"} = object, meta) do with {:ok, object} <- object @@ -121,76 +83,6 @@ def validate(%{"type" => "Delete"} = object, meta) do end end - def validate(%{"type" => "Like"} = object, meta) do - with {:ok, object} <- - object - |> LikeValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "ChatMessage"} = object, meta) do - with {:ok, object} <- - object - |> ChatMessageValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "Question"} = object, meta) do - with {:ok, object} <- - object - |> QuestionValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => type} = object, meta) when type in ~w[Audio Video] do - with {:ok, object} <- - object - |> AudioVideoValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "Article"} = object, meta) do - with {:ok, object} <- - object - |> ArticleNoteValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "Answer"} = object, meta) do - with {:ok, object} <- - object - |> AnswerValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "EmojiReact"} = object, meta) do - with {:ok, object} <- - object - |> EmojiReactValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - def validate( %{"type" => "Create", "object" => %{"type" => "ChatMessage"} = object} = create_activity, meta @@ -222,10 +114,60 @@ def validate( end end - def validate(%{"type" => "Announce"} = object, meta) do + def validate(%{"type" => type} = object, meta) + when type in ~w[Event Question Audio Video Article] do + validator = + case type do + "Event" -> EventValidator + "Question" -> QuestionValidator + "Audio" -> AudioVideoValidator + "Video" -> AudioVideoValidator + "Article" -> ArticleNoteValidator + end + with {:ok, object} <- object - |> AnnounceValidator.cast_and_validate() + |> validator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + object = stringify_keys(object) + + # Insert copy of hashtags as strings for the non-hashtag table indexing + tag = (object["tag"] || []) ++ Object.hashtags(%Object{data: object}) + object = Map.put(object, "tag", tag) + + {:ok, object, meta} + end + end + + def validate(%{"type" => type} = object, meta) + when type in ~w[Accept Reject Follow Update Like EmojiReact Announce + ChatMessage Answer] do + validator = + case type do + "Accept" -> AcceptRejectValidator + "Reject" -> AcceptRejectValidator + "Follow" -> FollowValidator + "Update" -> UpdateValidator + "Like" -> LikeValidator + "EmojiReact" -> EmojiReactValidator + "Announce" -> AnnounceValidator + "ChatMessage" -> ChatMessageValidator + "Answer" -> AnswerValidator + end + + with {:ok, object} <- + object + |> validator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + object = stringify_keys(object) + {:ok, object, meta} + end + end + + def validate(%{"type" => type} = object, meta) when type in ~w(Add Remove) do + with {:ok, object} <- + object + |> AddRemoveValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do object = stringify_keys(object) {:ok, object, meta} @@ -258,7 +200,7 @@ def cast_and_apply(%{"type" => "Article"} = object) do def cast_and_apply(o), do: {:error, {:validator_not_set, o}} - # is_struct/1 isn't present in Elixir 1.8.x + # is_struct/1 appears in Elixir 1.11 def stringify_keys(%{__struct__: _} = object) do object |> Map.from_struct() @@ -286,7 +228,7 @@ def fetch_actor(object) do def fetch_actor_and_object(object) do fetch_actor(object) - Object.normalize(object["object"], true) + Object.normalize(object["object"], fetch: true) :ok end end diff --git a/lib/pleroma/web/activity_pub/object_validator/validating.ex b/lib/pleroma/web/activity_pub/object_validator/validating.ex new file mode 100644 index 000000000..28e8d2498 --- /dev/null +++ b/lib/pleroma/web/activity_pub/object_validator/validating.ex @@ -0,0 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidator.Validating do + @callback validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} +end diff --git a/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex index 179beda58..b577a1044 100644 --- a/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do @@ -27,7 +27,7 @@ def cast_data(data) do |> cast(data, __schema__(:fields)) end - def validate_data(cng) do + defp validate_data(cng) do cng |> validate_required([:id, :type, :actor, :to, :cc, :object]) |> validate_inclusion(:type, ["Accept", "Reject"]) diff --git a/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex b/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex new file mode 100644 index 000000000..f885aabe4 --- /dev/null +++ b/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex @@ -0,0 +1,77 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator do + use Ecto.Schema + + import Ecto.Changeset + import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations + + require Pleroma.Constants + + alias Pleroma.EctoType.ActivityPub.ObjectValidators + alias Pleroma.User + + @primary_key false + + embedded_schema do + field(:id, ObjectValidators.ObjectID, primary_key: true) + field(:target) + field(:object, ObjectValidators.ObjectID) + field(:actor, ObjectValidators.ObjectID) + field(:type) + field(:to, ObjectValidators.Recipients, default: []) + field(:cc, ObjectValidators.Recipients, default: []) + end + + def cast_and_validate(data) do + {:ok, actor} = User.get_or_fetch_by_ap_id(data["actor"]) + + {:ok, actor} = maybe_refetch_user(actor) + + data + |> maybe_fix_data_for_mastodon(actor) + |> cast_data() + |> validate_data(actor) + end + + defp maybe_fix_data_for_mastodon(data, actor) do + # Mastodon sends pin/unpin objects without id, to, cc fields + data + |> Map.put_new("id", Pleroma.Web.ActivityPub.Utils.generate_activity_id()) + |> Map.put_new("to", [Pleroma.Constants.as_public()]) + |> Map.put_new("cc", [actor.follower_address]) + end + + defp cast_data(data) do + cast(%__MODULE__{}, data, __schema__(:fields)) + end + + defp validate_data(changeset, actor) do + changeset + |> validate_required([:id, :target, :object, :actor, :type, :to, :cc]) + |> validate_inclusion(:type, ~w(Add Remove)) + |> validate_actor_presence() + |> validate_collection_belongs_to_actor(actor) + |> validate_object_presence() + end + + defp validate_collection_belongs_to_actor(changeset, actor) do + validate_change(changeset, :target, fn :target, target -> + if target == actor.featured_address do + [] + else + [target: "collection doesn't belong to actor"] + end + end) + end + + defp maybe_refetch_user(%User{featured_address: address} = user) when is_binary(address) do + {:ok, user} + end + + defp maybe_refetch_user(%User{ap_id: ap_id}) do + Pleroma.Web.ActivityPub.Transmogrifier.upgrade_user_from_ap_id(ap_id) + end +end diff --git a/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex index 338957db8..576341790 100644 --- a/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do @@ -50,7 +50,7 @@ def fix_after_cast(cng) do cng end - def validate_data(data_cng) do + defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Announce"]) |> validate_required([:id, :type, :object, :actor, :to, :cc]) diff --git a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex index b9fbaf4f6..c9bd9e42d 100644 --- a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do @@ -50,7 +50,7 @@ def changeset(struct, data) do |> cast(data, __schema__(:fields)) end - def validate_data(data_cng) do + defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Answer"]) |> validate_required([:id, :inReplyTo, :name, :attributedTo, :actor]) diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex index 5b7dad517..39ef6dc29 100644 --- a/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations + alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -22,8 +23,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do field(:cc, ObjectValidators.Recipients, default: []) field(:bto, ObjectValidators.Recipients, default: []) field(:bcc, ObjectValidators.Recipients, default: []) - # TODO: Write type - field(:tag, {:array, :map}, default: []) + embeds_many(:tag, TagValidator) field(:type, :string) field(:name, :string) @@ -90,11 +90,12 @@ def changeset(struct, data) do data = fix(data) struct - |> cast(data, __schema__(:fields) -- [:attachment]) + |> cast(data, __schema__(:fields) -- [:attachment, :tag]) |> cast_embed(:attachment) + |> cast_embed(:tag) end - def validate_data(data_cng) do + defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Article", "Note"]) |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id]) diff --git a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex index f96fd54bf..4a0d1473d 100644 --- a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex @@ -1,12 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do use Ecto.Schema alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.Web.ActivityPub.ObjectValidators.UrlObjectValidator import Ecto.Changeset @@ -90,7 +89,7 @@ defp fix_url(data) do end end - def validate_data(cng) do + defp validate_data(cng) do cng |> validate_inclusion(:type, ~w[Document Audio Image Video]) |> validate_required([:mediaType, :url, :type]) diff --git a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex b/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex index 9b38aa4c2..27e14b16d 100644 --- a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations + alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -22,8 +23,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do field(:cc, ObjectValidators.Recipients, default: []) field(:bto, ObjectValidators.Recipients, default: []) field(:bcc, ObjectValidators.Recipients, default: []) - # TODO: Write type - field(:tag, {:array, :map}, default: []) + embeds_many(:tag, TagValidator) field(:type, :string) field(:name, :string) @@ -69,19 +69,33 @@ def cast_data(data) do |> changeset(data) end - defp fix_url(%{"url" => url} = data) when is_list(url) do - attachment = - Enum.find(url, fn x -> - mime_type = x["mimeType"] || x["mediaType"] || "" + defp find_attachment(url) do + mpeg_url = + Enum.find(url, fn + %{"mediaType" => mime_type, "tag" => tags} when is_list(tags) -> + mime_type == "application/x-mpegURL" - is_map(x) and String.starts_with?(mime_type, ["video/", "audio/"]) + _ -> + false end) - link_element = - Enum.find(url, fn x -> - mime_type = x["mimeType"] || x["mediaType"] || "" + url + |> Enum.concat(mpeg_url["tag"] || []) + |> Enum.find(fn + %{"mediaType" => mime_type} -> String.starts_with?(mime_type, ["video/", "audio/"]) + %{"mimeType" => mime_type} -> String.starts_with?(mime_type, ["video/", "audio/"]) + _ -> false + end) + end - is_map(x) and mime_type == "text/html" + defp fix_url(%{"url" => url} = data) when is_list(url) do + attachment = find_attachment(url) + + link_element = + Enum.find(url, fn + %{"mediaType" => "text/html"} -> true + %{"mimeType" => "text/html"} -> true + _ -> false end) data @@ -117,11 +131,12 @@ def changeset(struct, data) do data = fix(data) struct - |> cast(data, __schema__(:fields) -- [:attachment]) + |> cast(data, __schema__(:fields) -- [:attachment, :tag]) |> cast_embed(:attachment) + |> cast_embed(:tag) end - def validate_data(data_cng) do + defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Audio", "Video"]) |> validate_required([:id, :actor, :attributedTo, :type, :context, :attachment]) diff --git a/lib/pleroma/web/activity_pub/object_validators/block_validator.ex b/lib/pleroma/web/activity_pub/object_validators/block_validator.ex index 1dde77198..88948135f 100644 --- a/lib/pleroma/web/activity_pub/object_validators/block_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/block_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator do @@ -26,7 +26,7 @@ def cast_data(data) do |> cast(data, __schema__(:fields)) end - def validate_data(cng) do + defp validate_data(cng) do cng |> validate_required([:id, :type, :actor, :to, :cc, :object]) |> validate_inclusion(:type, ["Block"]) diff --git a/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex b/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex index 6acd4a771..b153156b0 100644 --- a/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator do @@ -67,7 +67,7 @@ def changeset(struct, data) do |> cast_embed(:attachment) end - def validate_data(data_cng) do + defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["ChatMessage"]) |> validate_required([:id, :actor, :to, :type, :published]) diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex index b3638cfc7..5f2c633bc 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do diff --git a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex index 603d87b8e..940430588 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do alias Pleroma.Object alias Pleroma.User + @spec validate_any_presence(Ecto.Changeset.t(), [atom()]) :: Ecto.Changeset.t() def validate_any_presence(cng, fields) do non_empty = fields @@ -29,13 +30,14 @@ def validate_any_presence(cng, fields) do end end + @spec validate_actor_presence(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t() def validate_actor_presence(cng, options \\ []) do field_name = Keyword.get(options, :field_name, :actor) cng |> validate_change(field_name, fn field_name, actor -> case User.get_cached_by_ap_id(actor) do - %User{deactivated: true} -> + %User{is_active: false} -> [{field_name, "user is deactivated"}] %User{} -> @@ -47,6 +49,7 @@ def validate_actor_presence(cng, options \\ []) do end) end + @spec validate_object_presence(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t() def validate_object_presence(cng, options \\ []) do field_name = Keyword.get(options, :field_name, :object) allowed_types = Keyword.get(options, :allowed_types, false) @@ -68,6 +71,7 @@ def validate_object_presence(cng, options \\ []) do end) end + @spec validate_object_or_user_presence(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t() def validate_object_or_user_presence(cng, options \\ []) do field_name = Keyword.get(options, :field_name, :object) options = Keyword.put(options, :field_name, field_name) @@ -83,6 +87,7 @@ def validate_object_or_user_presence(cng, options \\ []) do if actor_cng.valid?, do: actor_cng, else: object_cng end + @spec validate_host_match(Ecto.Changeset.t(), [atom()]) :: Ecto.Changeset.t() def validate_host_match(cng, fields \\ [:id, :actor]) do if same_domain?(cng, fields) do cng @@ -95,6 +100,7 @@ def validate_host_match(cng, fields \\ [:id, :actor]) do end end + @spec validate_fields_match(Ecto.Changeset.t(), [atom()]) :: Ecto.Changeset.t() def validate_fields_match(cng, fields) do if map_unique?(cng, fields) do cng @@ -122,12 +128,14 @@ defp map_unique?(cng, fields, func \\ & &1) do end) end + @spec same_domain?(Ecto.Changeset.t(), [atom()]) :: boolean() def same_domain?(cng, fields \\ [:actor, :object]) do map_unique?(cng, fields, fn value -> URI.parse(value).host end) end # This figures out if a user is able to create, delete or modify something # based on the domain and superuser status + @spec validate_modification_rights(Ecto.Changeset.t()) :: Ecto.Changeset.t() def validate_modification_rights(cng) do actor = User.get_cached_by_ap_id(get_field(cng, :actor)) diff --git a/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex index 7269f9ff0..7a31a99bf 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only # NOTES @@ -39,7 +39,7 @@ def cast_and_validate(data, meta \\ []) do |> validate_data(meta) end - def validate_data(cng, meta \\ []) do + defp validate_data(cng, meta) do cng |> validate_required([:id, :actor, :to, :type, :object]) |> validate_inclusion(:type, ["Create"]) diff --git a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex index 422ee07be..e06e442f4 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only # Code based on CreateChatMessageValidator @@ -79,7 +79,7 @@ defp fix(data, meta) do |> CommonFixes.fix_actor() end - def validate_data(cng, meta \\ []) do + defp validate_data(cng, meta) do cng |> validate_required([:actor, :type, :object]) |> validate_inclusion(:type, ["Create"]) diff --git a/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex index 9b9743c4a..a85a0298c 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateNoteValidator do diff --git a/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex b/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex index 2634e8d4d..7da67bf16 100644 --- a/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do @@ -53,7 +53,7 @@ def add_deleted_activity_id(cng) do Tombstone Video } - def validate_data(cng) do + defp validate_data(cng) do cng |> validate_required([:id, :type, :actor, :to, :cc, :object]) |> validate_inclusion(:type, ["Delete"]) diff --git a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex index 336c92d35..ec7566515 100644 --- a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do @@ -70,7 +70,7 @@ def validate_emoji(cng) do end end - def validate_data(data_cng) do + defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["EmojiReact"]) |> validate_required([:id, :type, :object, :actor, :context, :to, :cc, :content]) diff --git a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex index 0b4c99dc0..d42458ef5 100644 --- a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations + alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -23,8 +24,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do field(:cc, ObjectValidators.Recipients, default: []) field(:bto, ObjectValidators.Recipients, default: []) field(:bcc, ObjectValidators.Recipients, default: []) - # TODO: Write type - field(:tag, {:array, :map}, default: []) + embeds_many(:tag, TagValidator) field(:type, :string) field(:name, :string) @@ -81,11 +81,12 @@ def changeset(struct, data) do data = fix(data) struct - |> cast(data, __schema__(:fields) -- [:attachment]) + |> cast(data, __schema__(:fields) -- [:attachment, :tag]) |> cast_embed(:attachment) + |> cast_embed(:tag) end - def validate_data(data_cng) do + defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Event"]) |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id]) diff --git a/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex b/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex index ca2724616..239cee5e7 100644 --- a/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator do @@ -27,7 +27,7 @@ def cast_data(data) do |> cast(data, __schema__(:fields)) end - def validate_data(cng) do + defp validate_data(cng) do cng |> validate_required([:id, :type, :actor, :to, :cc, :object]) |> validate_inclusion(:type, ["Follow"]) diff --git a/lib/pleroma/web/activity_pub/object_validators/like_validator.ex b/lib/pleroma/web/activity_pub/object_validators/like_validator.ex index 493e4c247..509da507b 100644 --- a/lib/pleroma/web/activity_pub/object_validators/like_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/like_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do @@ -76,7 +76,7 @@ def fix_recipients(cng) do end end - def validate_data(data_cng) do + defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Like"]) |> validate_required([:id, :type, :object, :actor, :context, :to, :cc]) diff --git a/lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex b/lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex index 478b3b5cf..ddcd1be7c 100644 --- a/lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator do diff --git a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex index 9310485dc..7012e2e1d 100644 --- a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do @@ -10,6 +10,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -24,8 +25,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do field(:cc, ObjectValidators.Recipients, default: []) field(:bto, ObjectValidators.Recipients, default: []) field(:bcc, ObjectValidators.Recipients, default: []) - # TODO: Write type - field(:tag, {:array, :map}, default: []) + embeds_many(:tag, TagValidator) field(:type, :string) field(:content, :string) field(:context, :string) @@ -93,13 +93,14 @@ def changeset(struct, data) do data = fix(data) struct - |> cast(data, __schema__(:fields) -- [:anyOf, :oneOf, :attachment]) + |> cast(data, __schema__(:fields) -- [:anyOf, :oneOf, :attachment, :tag]) |> cast_embed(:attachment) |> cast_embed(:anyOf) |> cast_embed(:oneOf) + |> cast_embed(:tag) end - def validate_data(data_cng) do + defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Question"]) |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id]) diff --git a/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex b/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex new file mode 100644 index 000000000..751021585 --- /dev/null +++ b/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex @@ -0,0 +1,77 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.TagValidator do + use Ecto.Schema + + alias Pleroma.EctoType.ActivityPub.ObjectValidators + + import Ecto.Changeset + + @primary_key false + embedded_schema do + # Common + field(:type, :string) + field(:name, :string) + + # Mention, Hashtag + field(:href, ObjectValidators.Uri) + + # Emoji + embeds_one :icon, IconObjectValidator, primary_key: false do + field(:type, :string) + field(:url, ObjectValidators.Uri) + end + + field(:updated, ObjectValidators.DateTime) + field(:id, ObjectValidators.Uri) + end + + def cast_and_validate(data) do + data + |> cast_data() + end + + def cast_data(data) do + %__MODULE__{} + |> changeset(data) + end + + def changeset(struct, %{"type" => "Mention"} = data) do + struct + |> cast(data, [:type, :name, :href]) + |> validate_required([:type, :href]) + end + + def changeset(struct, %{"type" => "Hashtag", "name" => name} = data) do + name = + cond do + "#" <> name -> name + name -> name + end + |> String.downcase() + + data = Map.put(data, "name", name) + + struct + |> cast(data, [:type, :name, :href]) + |> validate_required([:type, :name]) + end + + def changeset(struct, %{"type" => "Emoji"} = data) do + data = Map.put(data, "name", String.trim(data["name"], ":")) + + struct + |> cast(data, [:type, :name, :updated, :id]) + |> cast_embed(:icon, with: &icon_changeset/2) + |> validate_required([:type, :name, :icon]) + end + + def icon_changeset(struct, data) do + struct + |> cast(data, [:type, :url]) + |> validate_inclusion(:type, ~w[Image]) + |> validate_required([:type, :url]) + end +end diff --git a/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex index 8cae94467..e8af60ffa 100644 --- a/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do @@ -38,7 +38,7 @@ def changeset(struct, data) do |> cast(data, __schema__(:fields)) end - def validate_data(data_cng) do + defp validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Undo"]) |> validate_required([:id, :type, :object, :actor, :to, :cc]) diff --git a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex index b4ba5ede0..6bb1dc7fa 100644 --- a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do @@ -28,7 +28,7 @@ def cast_data(data) do |> cast(data, __schema__(:fields)) end - def validate_data(cng) do + defp validate_data(cng) do cng |> validate_required([:id, :type, :actor, :to, :cc, :object]) |> validate_inclusion(:type, ["Update"]) diff --git a/lib/pleroma/web/activity_pub/pipeline.ex b/lib/pleroma/web/activity_pub/pipeline.ex index 98c32a42b..a0f2e0312 100644 --- a/lib/pleroma/web/activity_pub/pipeline.ex +++ b/lib/pleroma/web/activity_pub/pipeline.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Pipeline do @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do alias Pleroma.Config alias Pleroma.Object alias Pleroma.Repo + alias Pleroma.Utils alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.MRF alias Pleroma.Web.ActivityPub.ObjectValidator @@ -14,12 +15,19 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Federator + @side_effects Config.get([:pipeline, :side_effects], SideEffects) + @federator Config.get([:pipeline, :federator], Federator) + @object_validator Config.get([:pipeline, :object_validator], ObjectValidator) + @mrf Config.get([:pipeline, :mrf], MRF) + @activity_pub Config.get([:pipeline, :activity_pub], ActivityPub) + @config Config.get([:pipeline, :config], Config) + @spec common_pipeline(map(), keyword()) :: {:ok, Activity.t() | Object.t(), keyword()} | {:error, any()} def common_pipeline(object, meta) do - case Repo.transaction(fn -> do_common_pipeline(object, meta) end) do + case Repo.transaction(fn -> do_common_pipeline(object, meta) end, Utils.query_timeout()) do {:ok, {:ok, activity, meta}} -> - SideEffects.handle_after_transaction(meta) + @side_effects.handle_after_transaction(meta) {:ok, activity, meta} {:ok, value} -> @@ -33,19 +41,17 @@ def common_pipeline(object, meta) do end end - def do_common_pipeline(object, meta) do - with {_, {:ok, validated_object, meta}} <- - {:validate_object, ObjectValidator.validate(object, meta)}, - {_, {:ok, mrfd_object, meta}} <- - {:mrf_object, MRF.pipeline_filter(validated_object, meta)}, - {_, {:ok, activity, meta}} <- - {:persist_object, ActivityPub.persist(mrfd_object, meta)}, - {_, {:ok, activity, meta}} <- - {:execute_side_effects, SideEffects.handle(activity, meta)}, - {_, {:ok, _}} <- {:federation, maybe_federate(activity, meta)} do - {:ok, activity, meta} + def do_common_pipeline(%{__struct__: _}, _meta), do: {:error, :is_struct} + + def do_common_pipeline(message, meta) do + with {_, {:ok, message, meta}} <- {:validate, @object_validator.validate(message, meta)}, + {_, {:ok, message, meta}} <- {:mrf, @mrf.pipeline_filter(message, meta)}, + {_, {:ok, message, meta}} <- {:persist, @activity_pub.persist(message, meta)}, + {_, {:ok, message, meta}} <- {:side_effects, @side_effects.handle(message, meta)}, + {_, {:ok, _}} <- {:federation, maybe_federate(message, meta)} do + {:ok, message, meta} else - {:mrf_object, {:reject, message, _}} -> {:reject, message} + {:mrf, {:reject, message, _}} -> {:reject, message} e -> {:error, e} end end @@ -54,7 +60,7 @@ defp maybe_federate(%Object{}, _), do: {:ok, :not_federated} defp maybe_federate(%Activity{} = activity, meta) do with {:ok, local} <- Keyword.fetch(meta, :local) do - do_not_federate = meta[:do_not_federate] || !Config.get([:instance, :federating]) + do_not_federate = meta[:do_not_federate] || !@config.get([:instance, :federating]) if !do_not_federate and local and not Visibility.is_local_public?(activity) do activity = @@ -64,7 +70,7 @@ defp maybe_federate(%Activity{} = activity, meta) do activity end - Federator.publish(activity) + @federator.publish(activity) {:ok, :federated} else {:ok, :not_federated} diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 5ab3562bf..b12b2fc24 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Publisher do @@ -129,7 +129,7 @@ defp recipients(actor, activity) do fetchers = with %Activity{data: %{"type" => "Delete"}} <- activity, - %Object{id: object_id} <- Object.normalize(activity), + %Object{id: object_id} <- Object.normalize(activity, fetch: false), fetchers <- User.get_delivered_users_by_object_id(object_id), _ <- Delivery.delete_all_by_object_id(object_id) do fetchers diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index 6606e1780..6d60a074f 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Relay do diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 8556fca1d..5fe143c2b 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.SideEffects do @@ -27,11 +27,19 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do require Logger + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + @ap_streamer Pleroma.Config.get([:side_effects, :ap_streamer], ActivityPub) + @logger Pleroma.Config.get([:side_effects, :logger], Logger) + + @behaviour Pleroma.Web.ActivityPub.SideEffects.Handling + + @impl true def handle(object, meta \\ []) # Task this handles # - Follows # - Sends a notification + @impl true def handle( %{ data: %{ @@ -59,6 +67,7 @@ def handle( # - Rejects all existing follow activities for this person # - Updates the follow state # - Dismisses notification + @impl true def handle( %{ data: %{ @@ -85,6 +94,7 @@ def handle( # - Follows if possible # - Sends a notification # - Generates accept or reject if appropriate + @impl true def handle( %{ data: %{ @@ -126,6 +136,7 @@ def handle( # Tasks this handles: # - Unfollow and block + @impl true def handle( %{data: %{"type" => "Block", "object" => blocked_user, "actor" => blocking_user}} = object, @@ -144,6 +155,7 @@ def handle( # # For a local user, we also get a changeset with the full information, so we # can update non-federating, non-activitypub settings as well. + @impl true def handle(%{data: %{"type" => "Update", "object" => updated_object}} = object, meta) do if changeset = Keyword.get(meta, :user_update_changeset) do changeset @@ -162,6 +174,7 @@ def handle(%{data: %{"type" => "Update", "object" => updated_object}} = object, # Tasks this handles: # - Add like to object # - Set up notification + @impl true def handle(%{data: %{"type" => "Like"}} = object, meta) do liked_object = Object.get_by_ap_id(object.data["object"]) Utils.add_like_to_object(object, liked_object) @@ -179,6 +192,7 @@ def handle(%{data: %{"type" => "Like"}} = object, meta) do # - Increase replies count # - Set up ActivityExpiration # - Set up notifications + @impl true def handle(%{data: %{"type" => "Create"}} = activity, meta) do with {:ok, object, meta} <- handle_object_creation(meta[:object_data], meta), %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do @@ -207,6 +221,7 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do # - Add announce to object # - Set up notification # - Stream out the announce + @impl true def handle(%{data: %{"type" => "Announce"}} = object, meta) do announced_object = Object.get_by_ap_id(object.data["object"]) user = User.get_cached_by_ap_id(object.data["actor"]) @@ -224,6 +239,7 @@ def handle(%{data: %{"type" => "Announce"}} = object, meta) do {:ok, object, meta} end + @impl true def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, meta) do with undone_object <- Activity.get_by_ap_id(undone_object), :ok <- handle_undoing(undone_object) do @@ -234,6 +250,7 @@ def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, met # Tasks this handles: # - Add reaction to object # - Set up notification + @impl true def handle(%{data: %{"type" => "EmojiReact"}} = object, meta) do reacted_object = Object.get_by_ap_id(object.data["object"]) Utils.add_emoji_reaction_to_object(object, reacted_object) @@ -250,18 +267,19 @@ def handle(%{data: %{"type" => "EmojiReact"}} = object, meta) do # - Reduce the user note count # - Reduce the reply count # - Stream out the activity + @impl true def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object, meta) do deleted_object = - Object.normalize(deleted_object, false) || + Object.normalize(deleted_object, fetch: false) || User.get_cached_by_ap_id(deleted_object) result = case deleted_object do %Object{} -> - with {:ok, deleted_object, activity} <- Object.delete(deleted_object), + with {:ok, deleted_object, _activity} <- Object.delete(deleted_object), {_, actor} when is_binary(actor) <- {:actor, deleted_object.data["actor"]}, %User{} = user <- User.get_cached_by_ap_id(actor) do - User.remove_pinnned_activity(user, activity) + User.remove_pinned_object_id(user, deleted_object.data["id"]) {:ok, user} = ActivityPub.decrease_note_count_if_public(user, deleted_object) @@ -271,12 +289,12 @@ def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object, MessageReference.delete_for_object(deleted_object) - ActivityPub.stream_out(object) - ActivityPub.stream_out_participations(deleted_object, user) + @ap_streamer.stream_out(object) + @ap_streamer.stream_out_participations(deleted_object, user) :ok else {:actor, _} -> - Logger.error("The object doesn't have an actor: #{inspect(deleted_object)}") + @logger.error("The object doesn't have an actor: #{inspect(deleted_object)}") :no_object_actor end @@ -294,7 +312,65 @@ def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object, end end + # Tasks this handles: + # - adds pin to user + # - removes expiration job for pinned activity, if was set for expiration + @impl true + def handle(%{data: %{"type" => "Add"} = data} = object, meta) do + with %User{} = user <- User.get_cached_by_ap_id(data["actor"]), + {:ok, _user} <- User.add_pinned_object_id(user, data["object"]) do + # if pinned activity was scheduled for deletion, we remove job + if expiration = Pleroma.Workers.PurgeExpiredActivity.get_expiration(meta[:activity_id]) do + Oban.cancel_job(expiration.id) + end + + {:ok, object, meta} + else + nil -> + {:error, :user_not_found} + + {:error, changeset} -> + if changeset.errors[:pinned_objects] do + {:error, :pinned_statuses_limit_reached} + else + changeset.errors + end + end + end + + # Tasks this handles: + # - removes pin from user + # - removes corresponding Add activity + # - if activity had expiration, recreates activity expiration job + @impl true + def handle(%{data: %{"type" => "Remove"} = data} = object, meta) do + with %User{} = user <- User.get_cached_by_ap_id(data["actor"]), + {:ok, _user} <- User.remove_pinned_object_id(user, data["object"]) do + data["object"] + |> Activity.add_by_params_query(user.ap_id, user.featured_address) + |> Repo.delete_all() + + # if pinned activity was scheduled for deletion, we reschedule it for deletion + if meta[:expires_at] do + # MRF.ActivityExpirationPolicy used UTC timestamps for expires_at in original implementation + {:ok, expires_at} = + Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime.cast(meta[:expires_at]) + + Pleroma.Workers.PurgeExpiredActivity.enqueue(%{ + activity_id: meta[:activity_id], + expires_at: expires_at + }) + end + + {:ok, object, meta} + else + nil -> {:error, :user_not_found} + error -> error + end + end + # Nothing to do + @impl true def handle(object, meta) do {:ok, object, meta} end @@ -312,7 +388,7 @@ def handle_object_creation(%{"type" => "ChatMessage"} = object, meta) do {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) {:ok, cm_ref} = MessageReference.create(chat, object, user.ap_id != actor.ap_id) - Cachex.put( + @cachex.put( :chat_message_id_idempotency_key_cache, cm_ref.id, meta[:idempotency_key] @@ -439,6 +515,7 @@ defp add_notifications(meta, notifications) do |> Keyword.put(:notifications, notifications ++ existing) end + @impl true def handle_after_transaction(meta) do meta |> send_notifications() diff --git a/lib/pleroma/web/activity_pub/side_effects/handling.ex b/lib/pleroma/web/activity_pub/side_effects/handling.ex new file mode 100644 index 000000000..a82305155 --- /dev/null +++ b/lib/pleroma/web/activity_pub/side_effects/handling.ex @@ -0,0 +1,8 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.SideEffects.Handling do + @callback handle(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} + @callback handle_after_transaction(map()) :: map() +end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 565d32433..d27d0bed4 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier do @@ -32,18 +32,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do """ def fix_object(object, options \\ []) do object - |> strip_internal_fields - |> fix_actor - |> fix_url - |> fix_attachments - |> fix_context + |> strip_internal_fields() + |> fix_actor() + |> fix_url() + |> fix_attachments() + |> fix_context() |> fix_in_reply_to(options) - |> fix_emoji - |> fix_tag - |> set_sensitive - |> fix_content_map - |> fix_addressing - |> fix_summary + |> fix_emoji() + |> fix_tag() + |> fix_content_map() + |> fix_addressing() + |> fix_summary() |> fix_type(options) end @@ -315,10 +314,9 @@ def fix_tag(%{"tag" => tag} = object) when is_list(tag) do tags = tag |> Enum.filter(fn data -> data["type"] == "Hashtag" and data["name"] end) - |> Enum.map(fn %{"name" => name} -> - name - |> String.slice(1..-1) - |> String.downcase() + |> Enum.map(fn + %{"name" => "#" <> hashtag} -> String.downcase(hashtag) + %{"name" => hashtag} -> String.downcase(hashtag) end) Map.put(object, "tag", tag ++ tags) @@ -536,7 +534,7 @@ def handle_incoming( end def handle_incoming(%{"type" => type} = data, _options) - when type in ~w{Like EmojiReact Announce} do + when type in ~w{Like EmojiReact Announce Add Remove} do with :ok <- ObjectValidator.fetch_actor_and_object(data), {:ok, activity, _meta} <- Pipeline.common_pipeline(data, local: false) do @@ -566,7 +564,7 @@ def handle_incoming( Pipeline.common_pipeline(data, local: false) do {:ok, activity} else - {:error, {:validate_object, _}} = e -> + {:error, {:validate, _}} = e -> # Check if we have a create activity for this with {:ok, object_id} <- ObjectValidators.ObjectID.cast(data["object"]), %Activity{data: %{"actor" => actor}} <- @@ -653,7 +651,9 @@ def handle_incoming(_, _), do: :error @spec get_obj_helper(String.t(), Keyword.t()) :: {:ok, Object.t()} | nil def get_obj_helper(id, options \\ []) do - case Object.normalize(id, true, options) do + options = Keyword.put(options, :fetch, true) + + case Object.normalize(id, options) do %Object{} = object -> {:ok, object} _ -> nil end @@ -672,7 +672,7 @@ def get_embedded_obj_helper(%{"attributedTo" => attributed_to, "id" => object_id "actor" => attributed_to, "object" => data }) do - {:ok, Object.normalize(activity)} + {:ok, Object.normalize(activity, fetch: false)} else _ -> get_obj_helper(object_id) end @@ -740,7 +740,6 @@ def replies(_), do: [] # Prepares the object of an outgoing create activity. def prepare_object(object) do object - |> set_sensitive |> add_hashtags |> add_mention_tags |> add_emoji_tags @@ -763,7 +762,7 @@ def prepare_outgoing(%{"type" => activity_type, "object" => object_id} = data) when activity_type in ["Create", "Listen"] do object = object_id - |> Object.normalize() + |> Object.normalize(fetch: false) |> Map.get(:data) |> prepare_object @@ -779,7 +778,7 @@ def prepare_outgoing(%{"type" => activity_type, "object" => object_id} = data) def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do object = object_id - |> Object.normalize() + |> Object.normalize(fetch: false) data = if Visibility.is_private?(object) && object.data["actor"] == ap_id do @@ -919,7 +918,7 @@ def add_emoji_tags(object), do: object defp build_emoji_tag({name, url}) do %{ - "icon" => %{"url" => url, "type" => "Image"}, + "icon" => %{"url" => "#{URI.encode(url)}", "type" => "Image"}, "name" => ":" <> name <> ":", "type" => "Emoji", "updated" => "1970-01-01T00:00:00Z", @@ -931,15 +930,6 @@ def set_conversation(object) do Map.put(object, "conversation", object["context"]) end - def set_sensitive(%{"sensitive" => _} = object) do - object - end - - def set_sensitive(object) do - tags = object["tag"] || [] - Map.put(object, "sensitive", "nsfw" in tags) - end - def set_type(%{"type" => "Answer"} = object) do Map.put(object, "type", "Note") end @@ -1010,6 +1000,7 @@ def upgrade_user_from_ap_id(ap_id) do with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id), {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id), {:ok, user} <- update_user(user, data) do + {:ok, _pid} = Task.start(fn -> ActivityPub.pinned_fetch_task(user) end) TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id}) {:ok, user} else diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index ea1c3a04a..a4dc469dc 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Utils do diff --git a/lib/pleroma/web/activity_pub/views/object_view.ex b/lib/pleroma/web/activity_pub/views/object_view.ex index e555e9999..8a3e4d77b 100644 --- a/lib/pleroma/web/activity_pub/views/object_view.ex +++ b/lib/pleroma/web/activity_pub/views/object_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectView do @@ -18,7 +18,7 @@ def render("object.json", %{object: %Object{} = object}) do def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} = activity}) when activity_type in ["Create", "Listen"] do base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header() - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) additional = Transmogrifier.prepare_object(activity.data) @@ -29,7 +29,7 @@ def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} def render("object.json", %{object: %Activity{} = activity}) do base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header() - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) additional = Transmogrifier.prepare_object(activity.data) diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 93c9f436c..462f3b4a7 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -1,13 +1,15 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.UserView do use Pleroma.Web, :view alias Pleroma.Keys + alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User + alias Pleroma.Web.ActivityPub.ObjectView alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.Endpoint @@ -97,6 +99,7 @@ def render("user.json", %{user: user}) do "followers" => "#{user.ap_id}/followers", "inbox" => "#{user.ap_id}/inbox", "outbox" => "#{user.ap_id}/outbox", + "featured" => "#{user.ap_id}/collections/featured", "preferredUsername" => user.nickname, "name" => user.name, "summary" => user.bio, @@ -112,7 +115,8 @@ def render("user.json", %{user: user}) do "tag" => emoji_tags, # Note: key name is indeed "discoverable" (not an error) "discoverable" => user.is_discoverable, - "capabilities" => capabilities + "capabilities" => capabilities, + "alsoKnownAs" => user.also_known_as } |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user)) |> Map.merge(maybe_make_image(&User.banner_url/2, "image", user)) @@ -244,6 +248,24 @@ def render("activity_collection_page.json", %{ |> Map.merge(pagination) end + def render("featured.json", %{ + user: %{featured_address: featured_address, pinned_objects: pinned_objects} + }) do + objects = + pinned_objects + |> Enum.sort_by(fn {_, pinned_at} -> pinned_at end, &>=/2) + |> Enum.map(fn {id, _} -> + ObjectView.render("object.json", %{object: Object.get_cached_by_ap_id(id)}) + end) + + %{ + "id" => featured_address, + "type" => "OrderedCollection", + "orderedItems" => objects + } + |> Map.merge(Utils.make_json_ld_header()) + end + defp maybe_put_total_items(map, false, _total), do: map defp maybe_put_total_items(map, true, total) do diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex index 2cb5a2bd0..00234c0b0 100644 --- a/lib/pleroma/web/activity_pub/visibility.ex +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Visibility do @@ -56,11 +56,10 @@ def is_direct?(activity) do def is_list?(%{data: %{"listMessage" => _}}), do: true def is_list?(_), do: false - @spec visible_for_user?(Activity.t() | nil, User.t() | nil) :: boolean() + @spec visible_for_user?(Object.t() | Activity.t() | nil, User.t() | nil) :: boolean() def visible_for_user?(%Activity{actor: ap_id}, %User{ap_id: ap_id}), do: true - + def visible_for_user?(%Object{data: %{"actor" => ap_id}}, %User{ap_id: ap_id}), do: true def visible_for_user?(nil, _), do: false - def visible_for_user?(%Activity{data: %{"listMessage" => _}}, nil), do: false def visible_for_user?( @@ -73,16 +72,18 @@ def visible_for_user?( |> Pleroma.List.member?(user) end - def visible_for_user?(%Activity{} = activity, nil) do - if restrict_unauthenticated_access?(activity), + def visible_for_user?(%{__struct__: module} = message, nil) + when module in [Activity, Object] do + if restrict_unauthenticated_access?(message), do: false, - else: is_public?(activity) + else: is_public?(message) and not is_local_public?(message) end - def visible_for_user?(%Activity{} = activity, user) do + def visible_for_user?(%{__struct__: module} = message, user) + when module in [Activity, Object] do x = [user.ap_id | User.following(user)] - y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || []) - is_public?(activity) || Enum.any?(x, &(&1 in y)) + y = [message.data["actor"]] ++ message.data["to"] ++ (message.data["cc"] || []) + is_public?(message) || Enum.any?(x, &(&1 in y)) end def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do diff --git a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex index 5c2c282b3..839ac1a8d 100644 --- a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.AdminAPIController do @@ -25,13 +25,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, - %{scopes: ["read:accounts"], admin: true} + %{scopes: ["admin:read:accounts"]} when action in [:right_get, :show_user_credentials, :create_backup] ) plug( OAuthScopesPlug, - %{scopes: ["write:accounts"], admin: true} + %{scopes: ["admin:write:accounts"]} when action in [ :get_password_reset, :force_password_reset, @@ -48,19 +48,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, - %{scopes: ["read:statuses"], admin: true} + %{scopes: ["admin:read:statuses"]} when action in [:list_user_statuses, :list_instance_statuses] ) plug( OAuthScopesPlug, - %{scopes: ["read:chats"], admin: true} + %{scopes: ["admin:read:chats"]} when action in [:list_user_chats] ) plug( OAuthScopesPlug, - %{scopes: ["read"], admin: true} + %{scopes: ["admin:read"]} when action in [ :list_log, :stats, @@ -70,7 +70,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, - %{scopes: ["write"], admin: true} + %{scopes: ["admin:write"]} when action in [ :restart, :resend_confirmation_email, @@ -85,17 +85,18 @@ def list_instance_statuses(conn, %{"instance" => instance} = params) do with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true {page, page_size} = page_params(params) - activities = + result = ActivityPub.fetch_statuses(nil, %{ instance: instance, limit: page_size, offset: (page - 1) * page_size, - exclude_reblogs: not with_reblogs + exclude_reblogs: not with_reblogs, + total: true }) conn |> put_view(AdminAPI.StatusView) - |> render("index.json", %{activities: activities, as: :activity}) + |> render("index.json", %{total: result[:total], activities: result[:items], as: :activity}) end def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = params) do @@ -103,18 +104,21 @@ def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickna godmode = params["godmode"] == "true" || params["godmode"] == true with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do - {_, page_size} = page_params(params) + {page, page_size} = page_params(params) - activities = + result = ActivityPub.fetch_user_activities(user, nil, %{ limit: page_size, + offset: (page - 1) * page_size, godmode: godmode, - exclude_reblogs: not with_reblogs + exclude_reblogs: not with_reblogs, + pagination_type: :offset, + total: true }) conn |> put_view(AdminAPI.StatusView) - |> render("index.json", %{activities: activities, as: :activity}) + |> render("index.json", %{total: result[:total], activities: result[:items], as: :activity}) else _ -> {:error, :not_found} end @@ -402,7 +406,7 @@ defp configurable_from_database do if Config.get(:configurable_from_database) do :ok else - {:error, "To use this endpoint you need to enable configuration from database."} + {:error, "You must enable configurable_from_database in your config file."} end end @@ -415,7 +419,7 @@ def reload_emoji(conn, _params) do def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do users = Enum.map(nicknames, &User.get_cached_by_nickname/1) - User.toggle_confirmation(users) + User.confirm(users) ModerationLog.insert_log(%{actor: admin, subject: users, action: "confirm_email"}) diff --git a/lib/pleroma/web/admin_api/controllers/chat_controller.ex b/lib/pleroma/web/admin_api/controllers/chat_controller.ex index af8ff8292..ff20c8604 100644 --- a/lib/pleroma/web/admin_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/chat_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ChatController do @@ -21,12 +21,12 @@ defmodule Pleroma.Web.AdminAPI.ChatController do plug( OAuthScopesPlug, - %{scopes: ["read:chats"], admin: true} when action in [:show, :messages] + %{scopes: ["admin:read:chats"]} when action in [:show, :messages] ) plug( OAuthScopesPlug, - %{scopes: ["write:chats"], admin: true} when action in [:delete_message] + %{scopes: ["admin:write:chats"]} when action in [:delete_message] ) action_fallback(Pleroma.Web.AdminAPI.FallbackController) diff --git a/lib/pleroma/web/admin_api/controllers/config_controller.ex b/lib/pleroma/web/admin_api/controllers/config_controller.ex index 5d155af3d..a718d7b8d 100644 --- a/lib/pleroma/web/admin_api/controllers/config_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/config_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ConfigController do @@ -10,11 +10,11 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do alias Pleroma.Web.Plugs.OAuthScopesPlug plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action == :update) + plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action == :update) plug( OAuthScopesPlug, - %{scopes: ["read"], admin: true} + %{scopes: ["admin:read"]} when action in [:show, :descriptions] ) @@ -122,7 +122,7 @@ defp configurable_from_database do if Config.get(:configurable_from_database) do :ok else - {:error, "To use this endpoint you need to enable configuration from database."} + {:error, "You must enable configurable_from_database in your config file."} end end diff --git a/lib/pleroma/web/admin_api/controllers/fallback_controller.ex b/lib/pleroma/web/admin_api/controllers/fallback_controller.ex index 34d90db07..45d8815b5 100644 --- a/lib/pleroma/web/admin_api/controllers/fallback_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/fallback_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.FallbackController do diff --git a/lib/pleroma/web/admin_api/controllers/frontend_controller.ex b/lib/pleroma/web/admin_api/controllers/frontend_controller.ex index fac3522b8..722f51bd2 100644 --- a/lib/pleroma/web/admin_api/controllers/frontend_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/frontend_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.FrontendController do @@ -9,8 +9,8 @@ defmodule Pleroma.Web.AdminAPI.FrontendController do alias Pleroma.Web.Plugs.OAuthScopesPlug plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action == :install) - plug(OAuthScopesPlug, %{scopes: ["read"], admin: true} when action == :index) + plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action == :install) + plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action == :index) action_fallback(Pleroma.Web.AdminAPI.FallbackController) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.FrontendOperation diff --git a/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex b/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex index 37dbfeb72..a55857a0e 100644 --- a/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do @@ -15,8 +15,8 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.InstanceDocumentOperation - plug(OAuthScopesPlug, %{scopes: ["read"], admin: true} when action == :show) - plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action in [:update, :delete]) + plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action == :show) + plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action in [:update, :delete]) def show(conn, %{name: document_name}) do with {:ok, url} <- InstanceDocument.get(document_name), diff --git a/lib/pleroma/web/admin_api/controllers/invite_controller.ex b/lib/pleroma/web/admin_api/controllers/invite_controller.ex index 6a9b4038a..727ebd846 100644 --- a/lib/pleroma/web/admin_api/controllers/invite_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/invite_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.InviteController do @@ -14,11 +14,11 @@ defmodule Pleroma.Web.AdminAPI.InviteController do require Logger plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :index) + plug(OAuthScopesPlug, %{scopes: ["admin:read:invites"]} when action == :index) plug( OAuthScopesPlug, - %{scopes: ["write:invites"], admin: true} when action in [:create, :revoke, :email] + %{scopes: ["admin:write:invites"]} when action in [:create, :revoke, :email] ) action_fallback(Pleroma.Web.AdminAPI.FallbackController) diff --git a/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex b/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex index 6d92e9f7f..a6d7aaf54 100644 --- a/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do @@ -9,16 +9,18 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do alias Pleroma.Web.MediaProxy alias Pleroma.Web.Plugs.OAuthScopesPlug + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + plug(Pleroma.Web.ApiSpec.CastAndValidate) plug( OAuthScopesPlug, - %{scopes: ["read:media_proxy_caches"], admin: true} when action in [:index] + %{scopes: ["admin:read:media_proxy_caches"]} when action in [:index] ) plug( OAuthScopesPlug, - %{scopes: ["write:media_proxy_caches"], admin: true} when action in [:purge, :delete] + %{scopes: ["admin:write:media_proxy_caches"]} when action in [:purge, :delete] ) action_fallback(Pleroma.Web.AdminAPI.FallbackController) @@ -38,7 +40,7 @@ def index(%{assigns: %{user: _}} = conn, params) do defp fetch_entries(params) do MediaProxy.cache_table() - |> Cachex.stream!(Cachex.Query.create(true, :key)) + |> @cachex.stream!(Cachex.Query.create(true, :key)) |> filter_entries(params[:query]) end diff --git a/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex b/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex index 116a05a4d..005fe67e2 100644 --- a/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.OAuthAppController do @@ -17,7 +17,7 @@ defmodule Pleroma.Web.AdminAPI.OAuthAppController do plug( OAuthScopesPlug, - %{scopes: ["write"], admin: true} + %{scopes: ["admin:write"]} when action in [:create, :index, :update, :delete] ) diff --git a/lib/pleroma/web/admin_api/controllers/relay_controller.ex b/lib/pleroma/web/admin_api/controllers/relay_controller.ex index 611388447..c6bd43fea 100644 --- a/lib/pleroma/web/admin_api/controllers/relay_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/relay_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.RelayController do @@ -15,11 +15,11 @@ defmodule Pleroma.Web.AdminAPI.RelayController do plug( OAuthScopesPlug, - %{scopes: ["write:follows"], admin: true} + %{scopes: ["admin:write:follows"]} when action in [:follow, :unfollow] ) - plug(OAuthScopesPlug, %{scopes: ["read"], admin: true} when action == :index) + plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action == :index) action_fallback(Pleroma.Web.AdminAPI.FallbackController) diff --git a/lib/pleroma/web/admin_api/controllers/report_controller.ex b/lib/pleroma/web/admin_api/controllers/report_controller.ex index cc77cbfdf..d4a4935ee 100644 --- a/lib/pleroma/web/admin_api/controllers/report_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/report_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ReportController do @@ -19,11 +19,11 @@ defmodule Pleroma.Web.AdminAPI.ReportController do require Logger plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(OAuthScopesPlug, %{scopes: ["read:reports"], admin: true} when action in [:index, :show]) + plug(OAuthScopesPlug, %{scopes: ["admin:read:reports"]} when action in [:index, :show]) plug( OAuthScopesPlug, - %{scopes: ["write:reports"], admin: true} + %{scopes: ["admin:write:reports"]} when action in [:update, :notes_create, :notes_delete] ) diff --git a/lib/pleroma/web/admin_api/controllers/status_controller.ex b/lib/pleroma/web/admin_api/controllers/status_controller.ex index 2bb437cfe..7058def82 100644 --- a/lib/pleroma/web/admin_api/controllers/status_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/status_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.StatusController do @@ -15,11 +15,11 @@ defmodule Pleroma.Web.AdminAPI.StatusController do require Logger plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(OAuthScopesPlug, %{scopes: ["read:statuses"], admin: true} when action in [:index, :show]) + plug(OAuthScopesPlug, %{scopes: ["admin:read:statuses"]} when action in [:index, :show]) plug( OAuthScopesPlug, - %{scopes: ["write:statuses"], admin: true} when action in [:update, :delete] + %{scopes: ["admin:write:statuses"]} when action in [:update, :delete] ) action_fallback(Pleroma.Web.AdminAPI.FallbackController) diff --git a/lib/pleroma/web/admin_api/controllers/user_controller.ex b/lib/pleroma/web/admin_api/controllers/user_controller.ex index a2a1c875d..d3e4c18a3 100644 --- a/lib/pleroma/web/admin_api/controllers/user_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/user_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.UserController do @@ -13,21 +13,22 @@ defmodule Pleroma.Web.AdminAPI.UserController do alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.Pipeline alias Pleroma.Web.AdminAPI - alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.AdminAPI.Search alias Pleroma.Web.Plugs.OAuthScopesPlug @users_page_size 50 + plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug( OAuthScopesPlug, - %{scopes: ["read:accounts"], admin: true} - when action in [:list, :show] + %{scopes: ["admin:read:accounts"]} + when action in [:index, :show] ) plug( OAuthScopesPlug, - %{scopes: ["write:accounts"], admin: true} + %{scopes: ["admin:write:accounts"]} when action in [ :delete, :create, @@ -40,17 +41,23 @@ defmodule Pleroma.Web.AdminAPI.UserController do plug( OAuthScopesPlug, - %{scopes: ["write:follows"], admin: true} + %{scopes: ["admin:write:follows"]} when action in [:follow, :unfollow] ) + plug(:put_view, Pleroma.Web.AdminAPI.AccountView) + action_fallback(AdminAPI.FallbackController) - def delete(conn, %{"nickname" => nickname}) do - delete(conn, %{"nicknames" => [nickname]}) + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.UserOperation + + def delete(conn, %{nickname: nickname}) do + conn + |> Map.put(:body_params, %{nicknames: [nickname]}) + |> delete(%{}) end - def delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do + def delete(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do users = Enum.map(nicknames, &User.get_cached_by_nickname/1) Enum.each(users, fn user -> @@ -67,10 +74,16 @@ def delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do json(conn, nicknames) end - def follow(%{assigns: %{user: admin}} = conn, %{ - "follower" => follower_nick, - "followed" => followed_nick - }) do + def follow( + %{ + assigns: %{user: admin}, + body_params: %{ + follower: follower_nick, + followed: followed_nick + } + } = conn, + _ + ) do with %User{} = follower <- User.get_cached_by_nickname(follower_nick), %User{} = followed <- User.get_cached_by_nickname(followed_nick) do User.follow(follower, followed) @@ -86,10 +99,16 @@ def follow(%{assigns: %{user: admin}} = conn, %{ json(conn, "ok") end - def unfollow(%{assigns: %{user: admin}} = conn, %{ - "follower" => follower_nick, - "followed" => followed_nick - }) do + def unfollow( + %{ + assigns: %{user: admin}, + body_params: %{ + follower: follower_nick, + followed: followed_nick + } + } = conn, + _ + ) do with %User{} = follower <- User.get_cached_by_nickname(follower_nick), %User{} = followed <- User.get_cached_by_nickname(followed_nick) do User.unfollow(follower, followed) @@ -105,9 +124,10 @@ def unfollow(%{assigns: %{user: admin}} = conn, %{ json(conn, "ok") end - def create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do + def create(%{assigns: %{user: admin}, body_params: %{users: users}} = conn, _) do changesets = - Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} -> + users + |> Enum.map(fn %{nickname: nickname, email: email, password: password} -> user_data = %{ nickname: nickname, name: nickname, @@ -124,57 +144,54 @@ def create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do end) case Pleroma.Repo.transaction(changesets) do - {:ok, users} -> - res = - users + {:ok, users_map} -> + users = + users_map |> Map.values() |> Enum.map(fn user -> {:ok, user} = User.post_register_action(user) user end) - |> Enum.map(&AccountView.render("created.json", %{user: &1})) ModerationLog.insert_log(%{ actor: admin, - subjects: Map.values(users), + subjects: users, action: "create" }) - json(conn, res) + render(conn, "created_many.json", users: users) {:error, id, changeset, _} -> - res = + changesets = Enum.map(changesets.operations, fn - {current_id, {:changeset, _current_changeset, _}} when current_id == id -> - AccountView.render("create-error.json", %{changeset: changeset}) + {^id, {:changeset, _current_changeset, _}} -> + changeset {_, {:changeset, current_changeset, _}} -> - AccountView.render("create-error.json", %{changeset: current_changeset}) + current_changeset end) conn |> put_status(:conflict) - |> json(res) + |> render("create_errors.json", changesets: changesets) end end - def show(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do + def show(%{assigns: %{user: admin}} = conn, %{nickname: nickname}) do with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do - conn - |> put_view(AccountView) - |> render("show.json", %{user: user}) + render(conn, "show.json", %{user: user}) else _ -> {:error, :not_found} end end - def toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do + def toggle_activation(%{assigns: %{user: admin}} = conn, %{nickname: nickname}) do user = User.get_cached_by_nickname(nickname) - {:ok, updated_user} = User.deactivate(user, !user.deactivated) + {:ok, updated_user} = User.set_activation(user, !user.is_active) - action = if user.deactivated, do: "activate", else: "deactivate" + action = if !user.is_active, do: "activate", else: "deactivate" ModerationLog.insert_log(%{ actor: admin, @@ -182,14 +199,12 @@ def toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nicknam action: action }) - conn - |> put_view(AccountView) - |> render("show.json", %{user: updated_user}) + render(conn, "show.json", user: updated_user) end - def activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do + def activate(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do users = Enum.map(nicknames, &User.get_cached_by_nickname/1) - {:ok, updated_users} = User.deactivate(users, false) + {:ok, updated_users} = User.set_activation(users, true) ModerationLog.insert_log(%{ actor: admin, @@ -197,14 +212,12 @@ def activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do action: "activate" }) - conn - |> put_view(AccountView) - |> render("index.json", %{users: Keyword.values(updated_users)}) + render(conn, "index.json", users: Keyword.values(updated_users)) end - def deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do + def deactivate(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do users = Enum.map(nicknames, &User.get_cached_by_nickname/1) - {:ok, updated_users} = User.deactivate(users, true) + {:ok, updated_users} = User.set_activation(users, false) ModerationLog.insert_log(%{ actor: admin, @@ -212,12 +225,10 @@ def deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) d action: "deactivate" }) - conn - |> put_view(AccountView) - |> render("index.json", %{users: Keyword.values(updated_users)}) + render(conn, "index.json", users: Keyword.values(updated_users)) end - def approve(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do + def approve(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do users = Enum.map(nicknames, &User.get_cached_by_nickname/1) {:ok, updated_users} = User.approve(users) @@ -227,36 +238,27 @@ def approve(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do action: "approve" }) - conn - |> put_view(AccountView) - |> render("index.json", %{users: updated_users}) + render(conn, "index.json", users: updated_users) end - def list(conn, params) do + def index(conn, params) do {page, page_size} = page_params(params) - filters = maybe_parse_filters(params["filters"]) + filters = maybe_parse_filters(params[:filters]) search_params = %{ - query: params["query"], + query: params[:query], page: page, page_size: page_size, - tags: params["tags"], - name: params["name"], - email: params["email"], - actor_types: params["actor_types"] + tags: params[:tags], + name: params[:name], + email: params[:email], + actor_types: params[:actor_types] } |> Map.merge(filters) with {:ok, users, count} <- Search.user(search_params) do - json( - conn, - AccountView.render("index.json", - users: users, - count: count, - page_size: page_size - ) - ) + render(conn, "index.json", users: users, count: count, page_size: page_size) end end @@ -274,8 +276,8 @@ defp maybe_parse_filters(filters) do defp page_params(params) do { - fetch_integer_param(params, "page", 1), - fetch_integer_param(params, "page_size", @users_page_size) + fetch_integer_param(params, :page, 1), + fetch_integer_param(params, :page_size, @users_page_size) } end end diff --git a/lib/pleroma/web/admin_api/report.ex b/lib/pleroma/web/admin_api/report.ex index 8660d6520..259068f04 100644 --- a/lib/pleroma/web/admin_api/report.ex +++ b/lib/pleroma/web/admin_api/report.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.Report do diff --git a/lib/pleroma/web/admin_api/search.ex b/lib/pleroma/web/admin_api/search.ex index 0bfb8f022..eeeebdf4e 100644 --- a/lib/pleroma/web/admin_api/search.ex +++ b/lib/pleroma/web/admin_api/search.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.Search do diff --git a/lib/pleroma/web/admin_api/views/account_view.ex b/lib/pleroma/web/admin_api/views/account_view.ex index 8bac24d3e..e053a9b67 100644 --- a/lib/pleroma/web/admin_api/views/account_view.ex +++ b/lib/pleroma/web/admin_api/views/account_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.AccountView do @@ -69,21 +69,26 @@ def render("show.json", %{user: user}) do %{ "id" => user.id, + "email" => user.email, "avatar" => avatar, "nickname" => user.nickname, "display_name" => display_name, - "deactivated" => user.deactivated, + "is_active" => user.is_active, "local" => user.local, - "roles" => User.roles(user), + "roles" => roles(user), "tags" => user.tags || [], - "confirmation_pending" => user.confirmation_pending, - "approval_pending" => user.approval_pending, + "is_confirmed" => user.is_confirmed, + "is_approved" => user.is_approved, "url" => user.uri || user.ap_id, "registration_reason" => user.registration_reason, "actor_type" => user.actor_type } end + def render("created_many.json", %{users: users}) do + render_many(users, AccountView, "created.json", as: :user) + end + def render("created.json", %{user: user}) do %{ type: "success", @@ -95,7 +100,11 @@ def render("created.json", %{user: user}) do } end - def render("create-error.json", %{changeset: %Ecto.Changeset{changes: changes, errors: errors}}) do + def render("create_errors.json", %{changesets: changesets}) do + render_many(changesets, AccountView, "create_error.json", as: :changeset) + end + + def render("create_error.json", %{changeset: %Ecto.Changeset{changes: changes, errors: errors}}) do %{ type: "error", code: 409, @@ -139,4 +148,11 @@ defp parse_error(errors) do defp image_url(%{"url" => [%{"href" => href} | _]}), do: href defp image_url(_), do: nil + + defp roles(%{is_moderator: is_moderator, is_admin: is_admin}) do + %{ + admin: is_admin, + moderator: is_moderator + } + end end diff --git a/lib/pleroma/web/admin_api/views/chat_view.ex b/lib/pleroma/web/admin_api/views/chat_view.ex index 847df1423..2a2015ad1 100644 --- a/lib/pleroma/web/admin_api/views/chat_view.ex +++ b/lib/pleroma/web/admin_api/views/chat_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ChatView do diff --git a/lib/pleroma/web/admin_api/views/config_view.ex b/lib/pleroma/web/admin_api/views/config_view.ex index d2d8b5907..d29b4963d 100644 --- a/lib/pleroma/web/admin_api/views/config_view.ex +++ b/lib/pleroma/web/admin_api/views/config_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ConfigView do diff --git a/lib/pleroma/web/admin_api/views/frontend_view.ex b/lib/pleroma/web/admin_api/views/frontend_view.ex index 374841d0b..a3933a57d 100644 --- a/lib/pleroma/web/admin_api/views/frontend_view.ex +++ b/lib/pleroma/web/admin_api/views/frontend_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.FrontendView do diff --git a/lib/pleroma/web/admin_api/views/invite_view.ex b/lib/pleroma/web/admin_api/views/invite_view.ex index f93cb6916..c7e307bda 100644 --- a/lib/pleroma/web/admin_api/views/invite_view.ex +++ b/lib/pleroma/web/admin_api/views/invite_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.InviteView do diff --git a/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex b/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex index a803bda0b..1ec123048 100644 --- a/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex +++ b/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.MediaProxyCacheView do diff --git a/lib/pleroma/web/admin_api/views/moderation_log_view.ex b/lib/pleroma/web/admin_api/views/moderation_log_view.ex index 112f9e0e1..b3a9efff3 100644 --- a/lib/pleroma/web/admin_api/views/moderation_log_view.ex +++ b/lib/pleroma/web/admin_api/views/moderation_log_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ModerationLogView do @@ -21,6 +21,7 @@ def render("show.json", %{log_entry: log_entry}) do |> DateTime.to_unix() %{ + id: log_entry.id, data: log_entry.data, time: time, message: ModerationLog.get_log_entry_message(log_entry) diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex index 535556370..1c67b2458 100644 --- a/lib/pleroma/web/admin_api/views/report_view.ex +++ b/lib/pleroma/web/admin_api/views/report_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ReportView do @@ -19,8 +19,7 @@ def render("index.json", %{reports: reports}) do reports: reports[:items] |> Enum.map(&Report.extract_report_info/1) - |> Enum.map(&render(__MODULE__, "show.json", &1)) - |> Enum.reverse(), + |> Enum.map(&render(__MODULE__, "show.json", &1)), total: reports[:total] } end diff --git a/lib/pleroma/web/admin_api/views/status_view.ex b/lib/pleroma/web/admin_api/views/status_view.ex index 6042a22b6..48d639b41 100644 --- a/lib/pleroma/web/admin_api/views/status_view.ex +++ b/lib/pleroma/web/admin_api/views/status_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.StatusView do @@ -13,6 +13,10 @@ defmodule Pleroma.Web.AdminAPI.StatusView do defdelegate merge_account_views(user), to: AdminAPI.AccountView + def render("index.json", %{total: total} = opts) do + %{total: total, activities: safe_render_many(opts.activities, __MODULE__, "show.json", opts)} + end + def render("index.json", opts) do safe_render_many(opts.activities, __MODULE__, "show.json", opts) end diff --git a/lib/pleroma/web/api_spec.ex b/lib/pleroma/web/api_spec.ex index 93a5273e3..528cd9cf4 100644 --- a/lib/pleroma/web/api_spec.ex +++ b/lib/pleroma/web/api_spec.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec do @@ -11,10 +11,10 @@ defmodule Pleroma.Web.ApiSpec do @behaviour OpenApi @impl OpenApi - def spec do + def spec(opts \\ []) do %OpenApi{ servers: - if Phoenix.Endpoint.server?(:pleroma, Endpoint) do + if opts[:server_specific] do [ # Populate the Server info from a phoenix endpoint OpenApiSpex.Server.from_endpoint(Endpoint) @@ -23,9 +23,26 @@ def spec do [] end, info: %OpenApiSpex.Info{ - title: "Pleroma", - description: Application.spec(:pleroma, :description) |> to_string(), - version: Application.spec(:pleroma, :vsn) |> to_string() + title: "Pleroma API", + description: """ + This is documentation for client Pleroma API. Most of the endpoints and entities come + from Mastodon API and have custom extensions on top. + + While this document aims to be a complete guide to the client API Pleroma exposes, + the details are still being worked out. Some endpoints may have incomplete or poorly worded documentation. + You might want to check the following resources if something is not clear: + - [Legacy Pleroma-specific endpoint documentation](https://docs-develop.pleroma.social/backend/development/API/pleroma_api/) + - [Mastodon API documentation](https://docs.joinmastodon.org/client/intro/) + - [Differences in Mastodon API responses from vanilla Mastodon](https://docs-develop.pleroma.social/backend/development/API/differences_in_mastoapi_responses/) + + Please report such occurences on our [issue tracker](https://git.pleroma.social/pleroma/pleroma/-/issues). Feel free to submit API questions or proposals there too! + """, + # Strip environment from the version + version: Application.spec(:pleroma, :vsn) |> to_string() |> String.replace(~r/\+.*$/, ""), + extensions: %{ + # Logo path should be picked so that the path exists both on Pleroma instances and on api.pleroma.social + "x-logo": %{"url" => "/static/logo.svg", "altText" => "Pleroma logo"} + } }, # populate the paths from a phoenix router paths: OpenApiSpex.Paths.from_router(Router), @@ -45,15 +62,74 @@ def spec do authorizationUrl: "/oauth/authorize", tokenUrl: "/oauth/token", scopes: %{ - "read" => "read", - "write" => "write", - "follow" => "follow", - "push" => "push" + # TODO: Document granular scopes + "read" => "Read everything", + "write" => "Write everything", + "follow" => "Manage relationships", + "push" => "Web Push API subscriptions" } } } } } + }, + extensions: %{ + # Redoc-specific extension, every time a new tag is added it should be reflected here, + # otherwise it won't be shown. + "x-tagGroups": [ + %{ + "name" => "Accounts", + "tags" => ["Account actions", "Retrieve account information", "Scrobbles"] + }, + %{ + "name" => "Administration", + "tags" => [ + "Chat administration", + "Emoji pack administration", + "Frontend managment", + "Instance configuration", + "Instance documents", + "Invites", + "MediaProxy cache", + "OAuth application managment", + "Relays", + "Report managment", + "Status administration", + "User administration" + ] + }, + %{"name" => "Applications", "tags" => ["Applications", "Push subscriptions"]}, + %{ + "name" => "Current account", + "tags" => [ + "Account credentials", + "Backups", + "Blocks and mutes", + "Data import", + "Domain blocks", + "Follow requests", + "Mascot", + "Markers", + "Notifications" + ] + }, + %{"name" => "Instance", "tags" => ["Custom emojis"]}, + %{"name" => "Messaging", "tags" => ["Chats", "Conversations"]}, + %{ + "name" => "Statuses", + "tags" => [ + "Emoji reactions", + "Lists", + "Polls", + "Timelines", + "Retrieve status information", + "Scheduled statuses", + "Search", + "Status actions" + ] + }, + %{"name" => "Miscellaneous", "tags" => ["Emoji packs", "Reports", "Suggestions"]} + ] } } # discover request/response schemas from path specs diff --git a/lib/pleroma/web/api_spec/cast_and_validate.ex b/lib/pleroma/web/api_spec/cast_and_validate.ex index 6d1a7ebbc..d23a7dcb6 100644 --- a/lib/pleroma/web/api_spec/cast_and_validate.ex +++ b/lib/pleroma/web/api_spec/cast_and_validate.ex @@ -1,6 +1,6 @@ # Pleroma: A lightweight social networking server # Copyright © 2019-2020 Moxley Stratton, Mike Buhot , MPL-2.0 -# Copyright © 2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.CastAndValidate do @@ -15,6 +15,7 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do @behaviour Plug + alias OpenApiSpex.Plug.PutApiSpec alias Plug.Conn @impl Plug @@ -25,12 +26,10 @@ def init(opts) do end @impl Plug - def call(%{private: %{open_api_spex: private_data}} = conn, %{ - operation_id: operation_id, - render_error: render_error - }) do - spec = private_data.spec - operation = private_data.operation_lookup[operation_id] + + def call(conn, %{operation_id: operation_id, render_error: render_error}) do + {spec, operation_lookup} = PutApiSpec.get_spec_and_operation_lookup(conn) + operation = operation_lookup[operation_id] content_type = case Conn.get_req_header(conn, "content-type") do @@ -43,8 +42,7 @@ def call(%{private: %{open_api_spex: private_data}} = conn, %{ "application/json" end - private_data = Map.put(private_data, :operation_id, operation_id) - conn = Conn.put_private(conn, :open_api_spex, private_data) + conn = Conn.put_private(conn, :operation_id, operation_id) case cast_and_validate(spec, operation, conn, content_type, strict?()) do {:ok, conn} -> @@ -64,25 +62,22 @@ def call( private: %{ phoenix_controller: controller, phoenix_action: action, - open_api_spex: private_data + open_api_spex: %{spec_module: spec_module} } } = conn, opts ) do + {spec, operation_lookup} = PutApiSpec.get_spec_and_operation_lookup(conn) + operation = - case private_data.operation_lookup[{controller, action}] do + case operation_lookup[{controller, action}] do nil -> operation_id = controller.open_api_operation(action).operationId - operation = private_data.operation_lookup[operation_id] + operation = operation_lookup[operation_id] - operation_lookup = - private_data.operation_lookup - |> Map.put({controller, action}, operation) + operation_lookup = Map.put(operation_lookup, {controller, action}, operation) - OpenApiSpex.Plug.Cache.adapter().put( - private_data.spec_module, - {private_data.spec, operation_lookup} - ) + OpenApiSpex.Plug.Cache.adapter().put(spec_module, {spec, operation_lookup}) operation diff --git a/lib/pleroma/web/api_spec/helpers.ex b/lib/pleroma/web/api_spec/helpers.ex index 34de2ed57..6f67339e6 100644 --- a/lib/pleroma/web/api_spec/helpers.ex +++ b/lib/pleroma/web/api_spec/helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Helpers do @@ -63,7 +63,7 @@ def with_relationships_param do :with_relationships, :query, BooleanLike, - "Embed relationships into accounts." + "Embed relationships into accounts. **If this parameter is not set account's `pleroma.relationship` is going to be `null`.**" ) end diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 280100c3d..54e5ebc76 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.AccountOperation do @@ -26,7 +26,7 @@ def open_api_operation(action) do @spec create_operation() :: Operation.t() def create_operation do %Operation{ - tags: ["accounts"], + tags: ["Account credentials"], summary: "Register an account", description: "Creates a user and account records. Returns an account access token for the app that initiated the request. The app should save this token for later, and should wait for the user to confirm their account by clicking a link in their email inbox.", @@ -43,7 +43,7 @@ def create_operation do def verify_credentials_operation do %Operation{ - tags: ["accounts"], + tags: ["Account credentials"], description: "Test to make sure that the user token works.", summary: "Verify account credentials", operationId: "AccountController.verify_credentials", @@ -56,7 +56,7 @@ def verify_credentials_operation do def update_credentials_operation do %Operation{ - tags: ["accounts"], + tags: ["Account credentials"], summary: "Update account credentials", description: "Update the user's display and preferences.", operationId: "AccountController.update_credentials", @@ -71,8 +71,8 @@ def update_credentials_operation do def relationships_operation do %Operation{ - tags: ["accounts"], - summary: "Check relationships to other accounts", + tags: ["Retrieve account information"], + summary: "Relationship with current account", operationId: "AccountController.relationships", description: "Find out whether a given account is followed, blocked, muted, etc.", security: [%{"oAuth" => ["read:follows"]}], @@ -95,11 +95,14 @@ def relationships_operation do def show_operation do %Operation{ - tags: ["accounts"], + tags: ["Retrieve account information"], summary: "Account", operationId: "AccountController.show", description: "View information about a profile.", - parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}], + parameters: [ + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, + with_relationships_param() + ], responses: %{ 200 => Operation.response("Account", "application/json", Account), 401 => Operation.response("Error", "application/json", ApiError), @@ -110,8 +113,8 @@ def show_operation do def statuses_operation do %Operation{ - tags: ["accounts"], summary: "Statuses", + tags: ["Retrieve account information"], operationId: "AccountController.statuses", description: "Statuses posted to the given account. Public (for public statuses only), or user token + `read:statuses` (for private statuses the user is authorized to see)", @@ -130,7 +133,7 @@ def statuses_operation do :with_muted, :query, BooleanLike, - "Include statuses from muted acccounts." + "Include statuses from muted accounts." ), Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"), Operation.parameter(:exclude_replies, :query, BooleanLike, "Exclude replies"), @@ -144,7 +147,7 @@ def statuses_operation do :with_muted, :query, BooleanLike, - "Include reactions from muted acccounts." + "Include reactions from muted accounts." ) ] ++ pagination_params(), responses: %{ @@ -157,7 +160,7 @@ def statuses_operation do def followers_operation do %Operation{ - tags: ["accounts"], + tags: ["Retrieve account information"], summary: "Followers", operationId: "AccountController.followers", security: [%{"oAuth" => ["read:accounts"]}], @@ -176,7 +179,7 @@ def followers_operation do def following_operation do %Operation{ - tags: ["accounts"], + tags: ["Retrieve account information"], summary: "Following", operationId: "AccountController.following", security: [%{"oAuth" => ["read:accounts"]}], @@ -193,7 +196,7 @@ def following_operation do def lists_operation do %Operation{ - tags: ["accounts"], + tags: ["Retrieve account information"], summary: "Lists containing this account", operationId: "AccountController.lists", security: [%{"oAuth" => ["read:lists"]}], @@ -205,7 +208,7 @@ def lists_operation do def follow_operation do %Operation{ - tags: ["accounts"], + tags: ["Account actions"], summary: "Follow", operationId: "AccountController.follow", security: [%{"oAuth" => ["follow", "write:follows"]}], @@ -238,7 +241,7 @@ def follow_operation do def unfollow_operation do %Operation{ - tags: ["accounts"], + tags: ["Account actions"], summary: "Unfollow", operationId: "AccountController.unfollow", security: [%{"oAuth" => ["follow", "write:follows"]}], @@ -254,7 +257,7 @@ def unfollow_operation do def mute_operation do %Operation{ - tags: ["accounts"], + tags: ["Account actions"], summary: "Mute", operationId: "AccountController.mute", security: [%{"oAuth" => ["follow", "write:mutes"]}], @@ -284,7 +287,7 @@ def mute_operation do def unmute_operation do %Operation{ - tags: ["accounts"], + tags: ["Account actions"], summary: "Unmute", operationId: "AccountController.unmute", security: [%{"oAuth" => ["follow", "write:mutes"]}], @@ -298,7 +301,7 @@ def unmute_operation do def block_operation do %Operation{ - tags: ["accounts"], + tags: ["Account actions"], summary: "Block", operationId: "AccountController.block", security: [%{"oAuth" => ["follow", "write:blocks"]}], @@ -313,7 +316,7 @@ def block_operation do def unblock_operation do %Operation{ - tags: ["accounts"], + tags: ["Account actions"], summary: "Unblock", operationId: "AccountController.unblock", security: [%{"oAuth" => ["follow", "write:blocks"]}], @@ -327,7 +330,7 @@ def unblock_operation do def follow_by_uri_operation do %Operation{ - tags: ["accounts"], + tags: ["Account actions"], summary: "Follow by URI", operationId: "AccountController.follows", security: [%{"oAuth" => ["follow", "write:follows"]}], @@ -342,12 +345,12 @@ def follow_by_uri_operation do def mutes_operation do %Operation{ - tags: ["accounts"], - summary: "Muted accounts", + tags: ["Blocks and mutes"], + summary: "Retrieve list of mutes", operationId: "AccountController.mutes", description: "Accounts the user has muted.", security: [%{"oAuth" => ["follow", "read:mutes"]}], - parameters: pagination_params(), + parameters: [with_relationships_param() | pagination_params()], responses: %{ 200 => Operation.response("Accounts", "application/json", array_of_accounts()) } @@ -356,8 +359,8 @@ def mutes_operation do def blocks_operation do %Operation{ - tags: ["accounts"], - summary: "Blocked users", + tags: ["Blocks and mutes"], + summary: "Retrieve list of blocks", operationId: "AccountController.blocks", description: "View your blocks. See also accounts/:id/{block,unblock}", security: [%{"oAuth" => ["read:blocks"]}], @@ -370,7 +373,7 @@ def blocks_operation do def endorsements_operation do %Operation{ - tags: ["accounts"], + tags: ["Retrieve account information"], summary: "Endorsements", operationId: "AccountController.endorsements", description: "Not implemented", @@ -383,7 +386,7 @@ def endorsements_operation do def identity_proofs_operation do %Operation{ - tags: ["accounts"], + tags: ["Retrieve account information"], summary: "Identity proofs", operationId: "AccountController.identity_proofs", # Validators complains about unused path params otherwise @@ -614,6 +617,12 @@ defp update_credentials_request do nullable: true, description: "Allows automatically follow moved following accounts" }, + also_known_as: %Schema{ + type: :array, + items: %Schema{type: :string}, + nullable: true, + description: "List of alternate ActivityPub IDs" + }, pleroma_background_image: %Schema{ type: :string, nullable: true, @@ -644,6 +653,7 @@ defp update_credentials_request do pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}}, skip_thread_containment: false, allow_following_move: false, + also_known_as: ["https://foo.bar/users/foo"], discoverable: false, actor_type: "Person" } diff --git a/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex b/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex index d3e5dfc1c..57906445e 100644 --- a/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.ChatOperation do @@ -16,7 +16,7 @@ def open_api_operation(action) do def delete_message_operation do %Operation{ - tags: ["admin", "chat"], + tags: ["Chat administration"], summary: "Delete an individual chat message", operationId: "AdminAPI.ChatController.delete_message", parameters: [ @@ -33,7 +33,7 @@ def delete_message_operation do }, security: [ %{ - "oAuth" => ["write:chats"] + "oAuth" => ["admin:write:chats"] } ] } @@ -41,8 +41,8 @@ def delete_message_operation do def messages_operation do %Operation{ - tags: ["admin", "chat"], - summary: "Get the most recent messages of the chat", + tags: ["Chat administration"], + summary: "Get chat's messages", operationId: "AdminAPI.ChatController.messages", parameters: [Operation.parameter(:id, :path, :string, "The ID of the Chat")] ++ @@ -57,7 +57,7 @@ def messages_operation do }, security: [ %{ - "oAuth" => ["read:chats"] + "oAuth" => ["admin:read:chats"] } ] } @@ -65,7 +65,7 @@ def messages_operation do def show_operation do %Operation{ - tags: ["chat"], + tags: ["Chat administration"], summary: "Create a chat", operationId: "AdminAPI.ChatController.show", parameters: [ @@ -88,7 +88,7 @@ def show_operation do }, security: [ %{ - "oAuth" => ["read"] + "oAuth" => ["admin:read"] } ] } diff --git a/lib/pleroma/web/api_spec/operations/admin/config_operation.ex b/lib/pleroma/web/api_spec/operations/admin/config_operation.ex index 3a8380797..30c3433b7 100644 --- a/lib/pleroma/web/api_spec/operations/admin/config_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/config_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.ConfigOperation do @@ -16,8 +16,8 @@ def open_api_operation(action) do def show_operation do %Operation{ - tags: ["Admin", "Config"], - summary: "Get list of merged default settings with saved in database", + tags: ["Instance configuration"], + summary: "Retrieve instance configuration", operationId: "AdminAPI.ConfigController.show", parameters: [ Operation.parameter( @@ -28,7 +28,7 @@ def show_operation do ) | admin_api_params() ], - security: [%{"oAuth" => ["read"]}], + security: [%{"oAuth" => ["admin:read"]}], responses: %{ 200 => Operation.response("Config", "application/json", config_response()), 400 => Operation.response("Bad Request", "application/json", ApiError) @@ -38,10 +38,10 @@ def show_operation do def update_operation do %Operation{ - tags: ["Admin", "Config"], - summary: "Update config settings", + tags: ["Instance configuration"], + summary: "Update instance configuration", operationId: "AdminAPI.ConfigController.update", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], parameters: admin_api_params(), requestBody: request_body("Parameters", %Schema{ @@ -71,10 +71,10 @@ def update_operation do def descriptions_operation do %Operation{ - tags: ["Admin", "Config"], - summary: "Get JSON with config descriptions.", + tags: ["Instance configuration"], + summary: "Retrieve config description", operationId: "AdminAPI.ConfigController.descriptions", - security: [%{"oAuth" => ["read"]}], + security: [%{"oAuth" => ["admin:read"]}], parameters: admin_api_params(), responses: %{ 200 => diff --git a/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex b/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex index 96d4cdee7..566f1eeb1 100644 --- a/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.FrontendOperation do @@ -16,10 +16,10 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["Admin", "Reports"], - summary: "Get a list of available frontends", + tags: ["Frontend managment"], + summary: "Retrieve a list of available frontends", operationId: "AdminAPI.FrontendController.index", - security: [%{"oAuth" => ["read"]}], + security: [%{"oAuth" => ["admin:read"]}], responses: %{ 200 => Operation.response("Response", "application/json", list_of_frontends()), 403 => Operation.response("Forbidden", "application/json", ApiError) @@ -29,10 +29,10 @@ def index_operation do def install_operation do %Operation{ - tags: ["Admin", "Reports"], + tags: ["Frontend managment"], summary: "Install a frontend", operationId: "AdminAPI.FrontendController.install", - security: [%{"oAuth" => ["read"]}], + security: [%{"oAuth" => ["admin:read"]}], requestBody: request_body("Parameters", install_request(), required: true), responses: %{ 200 => Operation.response("Response", "application/json", list_of_frontends()), diff --git a/lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex b/lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex index a120ff4e8..79ceae970 100644 --- a/lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.InstanceDocumentOperation do @@ -15,10 +15,10 @@ def open_api_operation(action) do def show_operation do %Operation{ - tags: ["Admin", "InstanceDocument"], - summary: "Get the instance document", + tags: ["Instance documents"], + summary: "Retrieve an instance document", operationId: "AdminAPI.InstanceDocumentController.show", - security: [%{"oAuth" => ["read"]}], + security: [%{"oAuth" => ["admin:read"]}], parameters: [ Operation.parameter(:name, :path, %Schema{type: :string}, "The document name", required: true @@ -36,10 +36,10 @@ def show_operation do def update_operation do %Operation{ - tags: ["Admin", "InstanceDocument"], - summary: "Update the instance document", + tags: ["Instance documents"], + summary: "Update an instance document", operationId: "AdminAPI.InstanceDocumentController.update", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], requestBody: Helpers.request_body("Parameters", update_request()), parameters: [ Operation.parameter(:name, :path, %Schema{type: :string}, "The document name", @@ -74,10 +74,10 @@ defp update_request do def delete_operation do %Operation{ - tags: ["Admin", "InstanceDocument"], - summary: "Get the instance document", + tags: ["Instance documents"], + summary: "Delete an instance document", operationId: "AdminAPI.InstanceDocumentController.delete", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], parameters: [ Operation.parameter(:name, :path, %Schema{type: :string}, "The document name", required: true diff --git a/lib/pleroma/web/api_spec/operations/admin/invite_operation.ex b/lib/pleroma/web/api_spec/operations/admin/invite_operation.ex index 801024d75..704f082ba 100644 --- a/lib/pleroma/web/api_spec/operations/admin/invite_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/invite_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.InviteOperation do @@ -16,10 +16,10 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["Admin", "Invites"], + tags: ["Invites"], summary: "Get a list of generated invites", operationId: "AdminAPI.InviteController.index", - security: [%{"oAuth" => ["read:invites"]}], + security: [%{"oAuth" => ["admin:read:invites"]}], parameters: admin_api_params(), responses: %{ 200 => @@ -48,10 +48,10 @@ def index_operation do def create_operation do %Operation{ - tags: ["Admin", "Invites"], + tags: ["Invites"], summary: "Create an account registration invite token", operationId: "AdminAPI.InviteController.create", - security: [%{"oAuth" => ["write:invites"]}], + security: [%{"oAuth" => ["admin:write:invites"]}], parameters: admin_api_params(), requestBody: request_body("Parameters", %Schema{ @@ -69,10 +69,10 @@ def create_operation do def revoke_operation do %Operation{ - tags: ["Admin", "Invites"], + tags: ["Invites"], summary: "Revoke invite by token", operationId: "AdminAPI.InviteController.revoke", - security: [%{"oAuth" => ["write:invites"]}], + security: [%{"oAuth" => ["admin:write:invites"]}], parameters: admin_api_params(), requestBody: request_body( @@ -96,10 +96,10 @@ def revoke_operation do def email_operation do %Operation{ - tags: ["Admin", "Invites"], + tags: ["Invites"], summary: "Sends registration invite via email", operationId: "AdminAPI.InviteController.email", - security: [%{"oAuth" => ["write:invites"]}], + security: [%{"oAuth" => ["admin:write:invites"]}], parameters: admin_api_params(), requestBody: request_body( diff --git a/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex b/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex index ab45d6633..8f85ebf2d 100644 --- a/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.MediaProxyCacheOperation do @@ -16,10 +16,10 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["Admin", "MediaProxyCache"], - summary: "Fetch a paginated list of all banned MediaProxy URLs in Cachex", + tags: ["MediaProxy cache"], + summary: "Retrieve a list of banned MediaProxy URLs", operationId: "AdminAPI.MediaProxyCacheController.index", - security: [%{"oAuth" => ["read:media_proxy_caches"]}], + security: [%{"oAuth" => ["admin:read:media_proxy_caches"]}], parameters: [ Operation.parameter( :query, @@ -44,7 +44,7 @@ def index_operation do responses: %{ 200 => Operation.response( - "Array of banned MediaProxy URLs in Cachex", + "Array of MediaProxy URLs", "application/json", %Schema{ type: :object, @@ -68,10 +68,10 @@ def index_operation do def delete_operation do %Operation{ - tags: ["Admin", "MediaProxyCache"], - summary: "Remove a banned MediaProxy URL from Cachex", + tags: ["MediaProxy cache"], + summary: "Remove a banned MediaProxy URL", operationId: "AdminAPI.MediaProxyCacheController.delete", - security: [%{"oAuth" => ["write:media_proxy_caches"]}], + security: [%{"oAuth" => ["admin:write:media_proxy_caches"]}], parameters: admin_api_params(), requestBody: request_body( @@ -94,10 +94,10 @@ def delete_operation do def purge_operation do %Operation{ - tags: ["Admin", "MediaProxyCache"], - summary: "Purge and optionally ban a MediaProxy URL", + tags: ["MediaProxy cache"], + summary: "Purge a URL from MediaProxy cache and optionally ban it", operationId: "AdminAPI.MediaProxyCacheController.purge", - security: [%{"oAuth" => ["write:media_proxy_caches"]}], + security: [%{"oAuth" => ["admin:write:media_proxy_caches"]}], parameters: admin_api_params(), requestBody: request_body( diff --git a/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex b/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex index a75f3e622..35b029b19 100644 --- a/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do @@ -16,10 +16,10 @@ def open_api_operation(action) do def index_operation do %Operation{ - summary: "List OAuth apps", - tags: ["Admin", "oAuth Apps"], + summary: "Retrieve a list of OAuth applications", + tags: ["OAuth application managment"], operationId: "AdminAPI.OAuthAppController.index", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], parameters: [ Operation.parameter(:name, :query, %Schema{type: :string}, "App name"), Operation.parameter(:client_id, :query, %Schema{type: :string}, "Client ID"), @@ -69,12 +69,12 @@ def index_operation do def create_operation do %Operation{ - tags: ["Admin", "oAuth Apps"], - summary: "Create OAuth App", + tags: ["OAuth application managment"], + summary: "Create an OAuth application", operationId: "AdminAPI.OAuthAppController.create", requestBody: request_body("Parameters", create_request()), parameters: admin_api_params(), - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], responses: %{ 200 => Operation.response("App", "application/json", oauth_app()), 400 => Operation.response("Bad Request", "application/json", ApiError) @@ -84,11 +84,11 @@ def create_operation do def update_operation do %Operation{ - tags: ["Admin", "oAuth Apps"], - summary: "Update OAuth App", + tags: ["OAuth application managment"], + summary: "Update OAuth application", operationId: "AdminAPI.OAuthAppController.update", parameters: [id_param() | admin_api_params()], - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], requestBody: request_body("Parameters", update_request()), responses: %{ 200 => Operation.response("App", "application/json", oauth_app()), @@ -102,11 +102,11 @@ def update_operation do def delete_operation do %Operation{ - tags: ["Admin", "oAuth Apps"], - summary: "Delete OAuth App", + tags: ["OAuth application managment"], + summary: "Delete OAuth application", operationId: "AdminAPI.OAuthAppController.delete", parameters: [id_param() | admin_api_params()], - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], responses: %{ 204 => no_content_response(), 400 => no_content_response() diff --git a/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex b/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex index f754bb9f5..c55c84fee 100644 --- a/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.RelayOperation do @@ -15,10 +15,10 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["Admin", "Relays"], - summary: "List Relays", + tags: ["Relays"], + summary: "Retrieve a list of relays", operationId: "AdminAPI.RelayController.index", - security: [%{"oAuth" => ["read"]}], + security: [%{"oAuth" => ["admin:read"]}], parameters: admin_api_params(), responses: %{ 200 => @@ -37,10 +37,10 @@ def index_operation do def follow_operation do %Operation{ - tags: ["Admin", "Relays"], - summary: "Follow a Relay", + tags: ["Relays"], + summary: "Follow a relay", operationId: "AdminAPI.RelayController.follow", - security: [%{"oAuth" => ["write:follows"]}], + security: [%{"oAuth" => ["admin:write:follows"]}], parameters: admin_api_params(), requestBody: request_body("Parameters", relay_url()), responses: %{ @@ -51,10 +51,10 @@ def follow_operation do def unfollow_operation do %Operation{ - tags: ["Admin", "Relays"], - summary: "Unfollow a Relay", + tags: ["Relays"], + summary: "Unfollow a relay", operationId: "AdminAPI.RelayController.unfollow", - security: [%{"oAuth" => ["write:follows"]}], + security: [%{"oAuth" => ["admin:write:follows"]}], parameters: admin_api_params(), requestBody: request_body("Parameters", relay_unfollow()), responses: %{ diff --git a/lib/pleroma/web/api_spec/operations/admin/report_operation.ex b/lib/pleroma/web/api_spec/operations/admin/report_operation.ex index 3bb7ec49e..8d7577505 100644 --- a/lib/pleroma/web/api_spec/operations/admin/report_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/report_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do @@ -19,10 +19,10 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["Admin", "Reports"], - summary: "Get a list of reports", + tags: ["Report managment"], + summary: "Retrieve a list of reports", operationId: "AdminAPI.ReportController.index", - security: [%{"oAuth" => ["read:reports"]}], + security: [%{"oAuth" => ["admin:read:reports"]}], parameters: [ Operation.parameter( :state, @@ -69,11 +69,11 @@ def index_operation do def show_operation do %Operation{ - tags: ["Admin", "Reports"], - summary: "Get an individual report", + tags: ["Report managment"], + summary: "Retrieve a report", operationId: "AdminAPI.ReportController.show", parameters: [id_param() | admin_api_params()], - security: [%{"oAuth" => ["read:reports"]}], + security: [%{"oAuth" => ["admin:read:reports"]}], responses: %{ 200 => Operation.response("Report", "application/json", report()), 404 => Operation.response("Not Found", "application/json", ApiError) @@ -83,10 +83,10 @@ def show_operation do def update_operation do %Operation{ - tags: ["Admin", "Reports"], - summary: "Change the state of one or multiple reports", + tags: ["Report managment"], + summary: "Change state of specified reports", operationId: "AdminAPI.ReportController.update", - security: [%{"oAuth" => ["write:reports"]}], + security: [%{"oAuth" => ["admin:write:reports"]}], parameters: admin_api_params(), requestBody: request_body("Parameters", update_request(), required: true), responses: %{ @@ -99,8 +99,8 @@ def update_operation do def notes_create_operation do %Operation{ - tags: ["Admin", "Reports"], - summary: "Create report note", + tags: ["Report managment"], + summary: "Add a note to the report", operationId: "AdminAPI.ReportController.notes_create", parameters: [id_param() | admin_api_params()], requestBody: @@ -110,7 +110,7 @@ def notes_create_operation do content: %Schema{type: :string, description: "The message"} } }), - security: [%{"oAuth" => ["write:reports"]}], + security: [%{"oAuth" => ["admin:write:reports"]}], responses: %{ 204 => no_content_response(), 404 => Operation.response("Not Found", "application/json", ApiError) @@ -120,15 +120,15 @@ def notes_create_operation do def notes_delete_operation do %Operation{ - tags: ["Admin", "Reports"], - summary: "Delete report note", + tags: ["Report managment"], + summary: "Delete note attached to the report", operationId: "AdminAPI.ReportController.notes_delete", parameters: [ Operation.parameter(:report_id, :path, :string, "Report ID"), Operation.parameter(:id, :path, :string, "Note ID") | admin_api_params() ], - security: [%{"oAuth" => ["write:reports"]}], + security: [%{"oAuth" => ["admin:write:reports"]}], responses: %{ 204 => no_content_response(), 404 => Operation.response("Not Found", "application/json", ApiError) @@ -136,11 +136,11 @@ def notes_delete_operation do } end - defp report_state do + def report_state do %Schema{type: :string, enum: ["open", "closed", "resolved"]} end - defp id_param do + def id_param do Operation.parameter(:id, :path, FlakeID, "Report ID", example: "9umDrYheeY451cQnEe", required: true @@ -182,7 +182,7 @@ defp account_admin do properties: Map.merge(Account.schema().properties, %{ nickname: %Schema{type: :string}, - deactivated: %Schema{type: :boolean}, + is_active: %Schema{type: :boolean}, local: %Schema{type: :boolean}, roles: %Schema{ type: :object, @@ -191,7 +191,7 @@ defp account_admin do moderator: %Schema{type: :boolean} } }, - confirmation_pending: %Schema{type: :boolean} + is_confirmed: %Schema{type: :boolean} }) } end diff --git a/lib/pleroma/web/api_spec/operations/admin/status_operation.ex b/lib/pleroma/web/api_spec/operations/admin/status_operation.ex index c105838a4..d25ab5247 100644 --- a/lib/pleroma/web/api_spec/operations/admin/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/status_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do @@ -21,9 +21,10 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["Admin", "Statuses"], + tags: ["Status administration"], operationId: "AdminAPI.StatusController.index", - security: [%{"oAuth" => ["read:statuses"]}], + summary: "Get all statuses", + security: [%{"oAuth" => ["admin:read:statuses"]}], parameters: [ Operation.parameter( :godmode, @@ -69,11 +70,11 @@ def index_operation do def show_operation do %Operation{ - tags: ["Admin", "Statuses"], - summary: "Show Status", + tags: ["Status adminitration)"], + summary: "Get status", operationId: "AdminAPI.StatusController.show", parameters: [id_param() | admin_api_params()], - security: [%{"oAuth" => ["read:statuses"]}], + security: [%{"oAuth" => ["admin:read:statuses"]}], responses: %{ 200 => Operation.response("Status", "application/json", status()), 404 => Operation.response("Not Found", "application/json", ApiError) @@ -83,11 +84,11 @@ def show_operation do def update_operation do %Operation{ - tags: ["Admin", "Statuses"], - summary: "Change the scope of an individual reported status", + tags: ["Status adminitration)"], + summary: "Change the scope of a status", operationId: "AdminAPI.StatusController.update", parameters: [id_param() | admin_api_params()], - security: [%{"oAuth" => ["write:statuses"]}], + security: [%{"oAuth" => ["admin:write:statuses"]}], requestBody: request_body("Parameters", update_request(), required: true), responses: %{ 200 => Operation.response("Status", "application/json", Status), @@ -98,11 +99,11 @@ def update_operation do def delete_operation do %Operation{ - tags: ["Admin", "Statuses"], - summary: "Delete an individual reported status", + tags: ["Status adminitration)"], + summary: "Delete status", operationId: "AdminAPI.StatusController.delete", parameters: [id_param() | admin_api_params()], - security: [%{"oAuth" => ["write:statuses"]}], + security: [%{"oAuth" => ["admin:write:statuses"]}], responses: %{ 200 => empty_object_response(), 404 => Operation.response("Not Found", "application/json", ApiError) @@ -132,7 +133,7 @@ def admin_account do avatar: %Schema{type: :string}, nickname: %Schema{type: :string}, display_name: %Schema{type: :string}, - deactivated: %Schema{type: :boolean}, + is_active: %Schema{type: :boolean}, local: %Schema{type: :boolean}, roles: %Schema{ type: :object, @@ -142,7 +143,7 @@ def admin_account do } }, tags: %Schema{type: :string}, - confirmation_pending: %Schema{type: :string} + is_confirmed: %Schema{type: :string} } } end diff --git a/lib/pleroma/web/api_spec/operations/admin/user_operation.ex b/lib/pleroma/web/api_spec/operations/admin/user_operation.ex new file mode 100644 index 000000000..c9d0bfd7c --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/admin/user_operation.ex @@ -0,0 +1,389 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Admin.UserOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.ActorType + alias Pleroma.Web.ApiSpec.Schemas.ApiError + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %Operation{ + tags: ["User administration"], + summary: "List users", + operationId: "AdminAPI.UserController.index", + security: [%{"oAuth" => ["admin:read:accounts"]}], + parameters: [ + Operation.parameter(:filters, :query, :string, "Comma separated list of filters"), + Operation.parameter(:query, :query, :string, "Search users query"), + Operation.parameter(:name, :query, :string, "Search by display name"), + Operation.parameter(:email, :query, :string, "Search by email"), + Operation.parameter(:page, :query, :integer, "Page Number"), + Operation.parameter(:page_size, :query, :integer, "Number of users to return per page"), + Operation.parameter( + :actor_types, + :query, + %Schema{type: :array, items: ActorType}, + "Filter by actor type" + ), + Operation.parameter( + :tags, + :query, + %Schema{type: :array, items: %Schema{type: :string}}, + "Filter by tags" + ) + | admin_api_params() + ], + responses: %{ + 200 => + Operation.response( + "Response", + "application/json", + %Schema{ + type: :object, + properties: %{ + users: %Schema{type: :array, items: user()}, + count: %Schema{type: :integer}, + page_size: %Schema{type: :integer} + } + } + ), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def create_operation do + %Operation{ + tags: ["User administration"], + summary: "Create a single or multiple users", + operationId: "AdminAPI.UserController.create", + security: [%{"oAuth" => ["admin:write:accounts"]}], + parameters: admin_api_params(), + requestBody: + request_body( + "Parameters", + %Schema{ + description: "POST body for creating users", + type: :object, + properties: %{ + users: %Schema{ + type: :array, + items: %Schema{ + type: :object, + properties: %{ + nickname: %Schema{type: :string}, + email: %Schema{type: :string}, + password: %Schema{type: :string} + } + } + } + } + } + ), + responses: %{ + 200 => + Operation.response("Response", "application/json", %Schema{ + type: :array, + items: %Schema{ + type: :object, + properties: %{ + code: %Schema{type: :integer}, + type: %Schema{type: :string}, + data: %Schema{ + type: :object, + properties: %{ + email: %Schema{type: :string, format: :email}, + nickname: %Schema{type: :string} + } + } + } + } + }), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 409 => + Operation.response("Conflict", "application/json", %Schema{ + type: :array, + items: %Schema{ + type: :object, + properties: %{ + code: %Schema{type: :integer}, + error: %Schema{type: :string}, + type: %Schema{type: :string}, + data: %Schema{ + type: :object, + properties: %{ + email: %Schema{type: :string, format: :email}, + nickname: %Schema{type: :string} + } + } + } + } + }) + } + } + end + + def show_operation do + %Operation{ + tags: ["User administration"], + summary: "Show user", + operationId: "AdminAPI.UserController.show", + security: [%{"oAuth" => ["admin:read:accounts"]}], + parameters: [ + Operation.parameter( + :nickname, + :path, + :string, + "User nickname or ID" + ) + | admin_api_params() + ], + responses: %{ + 200 => Operation.response("Response", "application/json", user()), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def follow_operation do + %Operation{ + tags: ["User administration"], + summary: "Follow", + operationId: "AdminAPI.UserController.follow", + security: [%{"oAuth" => ["admin:write:follows"]}], + parameters: admin_api_params(), + requestBody: + request_body( + "Parameters", + %Schema{ + type: :object, + properties: %{ + follower: %Schema{type: :string, description: "Follower nickname"}, + followed: %Schema{type: :string, description: "Followed nickname"} + } + } + ), + responses: %{ + 200 => Operation.response("Response", "application/json", %Schema{type: :string}), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def unfollow_operation do + %Operation{ + tags: ["User administration"], + summary: "Unfollow", + operationId: "AdminAPI.UserController.unfollow", + security: [%{"oAuth" => ["admin:write:follows"]}], + parameters: admin_api_params(), + requestBody: + request_body( + "Parameters", + %Schema{ + type: :object, + properties: %{ + follower: %Schema{type: :string, description: "Follower nickname"}, + followed: %Schema{type: :string, description: "Followed nickname"} + } + } + ), + responses: %{ + 200 => Operation.response("Response", "application/json", %Schema{type: :string}), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def approve_operation do + %Operation{ + tags: ["User administration"], + summary: "Approve multiple users", + operationId: "AdminAPI.UserController.approve", + security: [%{"oAuth" => ["admin:write:accounts"]}], + parameters: admin_api_params(), + requestBody: + request_body( + "Parameters", + %Schema{ + description: "POST body for deleting multiple users", + type: :object, + properties: %{ + nicknames: %Schema{ + type: :array, + items: %Schema{type: :string} + } + } + } + ), + responses: %{ + 200 => + Operation.response("Response", "application/json", %Schema{ + type: :object, + properties: %{user: %Schema{type: :array, items: user()}} + }), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def toggle_activation_operation do + %Operation{ + tags: ["User administration"], + summary: "Toggle user activation", + operationId: "AdminAPI.UserController.toggle_activation", + security: [%{"oAuth" => ["admin:write:accounts"]}], + parameters: [ + Operation.parameter(:nickname, :path, :string, "User nickname") + | admin_api_params() + ], + responses: %{ + 200 => Operation.response("Response", "application/json", user()), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def activate_operation do + %Operation{ + tags: ["User administration"], + summary: "Activate multiple users", + operationId: "AdminAPI.UserController.activate", + security: [%{"oAuth" => ["admin:write:accounts"]}], + parameters: admin_api_params(), + requestBody: + request_body( + "Parameters", + %Schema{ + description: "POST body for deleting multiple users", + type: :object, + properties: %{ + nicknames: %Schema{ + type: :array, + items: %Schema{type: :string} + } + } + } + ), + responses: %{ + 200 => + Operation.response("Response", "application/json", %Schema{ + type: :object, + properties: %{user: %Schema{type: :array, items: user()}} + }), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def deactivate_operation do + %Operation{ + tags: ["User administration"], + summary: "Deactivates multiple users", + operationId: "AdminAPI.UserController.deactivate", + security: [%{"oAuth" => ["admin:write:accounts"]}], + parameters: admin_api_params(), + requestBody: + request_body( + "Parameters", + %Schema{ + description: "POST body for deleting multiple users", + type: :object, + properties: %{ + nicknames: %Schema{ + type: :array, + items: %Schema{type: :string} + } + } + } + ), + responses: %{ + 200 => + Operation.response("Response", "application/json", %Schema{ + type: :object, + properties: %{user: %Schema{type: :array, items: user()}} + }), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + def delete_operation do + %Operation{ + tags: ["User administration"], + summary: "Removes a single or multiple users", + operationId: "AdminAPI.UserController.delete", + security: [%{"oAuth" => ["admin:write:accounts"]}], + parameters: [ + Operation.parameter( + :nickname, + :query, + :string, + "User nickname" + ) + | admin_api_params() + ], + requestBody: + request_body( + "Parameters", + %Schema{ + description: "POST body for deleting multiple users", + type: :object, + properties: %{ + nicknames: %Schema{ + type: :array, + items: %Schema{type: :string} + } + } + } + ), + responses: %{ + 200 => + Operation.response("Response", "application/json", %Schema{ + description: "Array of nicknames", + type: :array, + items: %Schema{type: :string} + }), + 403 => Operation.response("Forbidden", "application/json", ApiError) + } + } + end + + defp user do + %Schema{ + type: :object, + properties: %{ + id: %Schema{type: :string}, + email: %Schema{type: :string, format: :email}, + avatar: %Schema{type: :string, format: :uri}, + nickname: %Schema{type: :string}, + display_name: %Schema{type: :string}, + is_active: %Schema{type: :boolean}, + local: %Schema{type: :boolean}, + roles: %Schema{ + type: :object, + properties: %{ + admin: %Schema{type: :boolean}, + moderator: %Schema{type: :boolean} + } + }, + tags: %Schema{type: :array, items: %Schema{type: :string}}, + is_confirmed: %Schema{type: :boolean}, + is_approved: %Schema{type: :boolean}, + url: %Schema{type: :string, format: :uri}, + registration_reason: %Schema{type: :string, nullable: true}, + actor_type: %Schema{type: :string} + } + } + end +end diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex index ae01cbbec..dfb1c7170 100644 --- a/lib/pleroma/web/api_spec/operations/app_operation.ex +++ b/lib/pleroma/web/api_spec/operations/app_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.AppOperation do @@ -16,7 +16,7 @@ def open_api_operation(action) do @spec create_operation() :: Operation.t() def create_operation do %Operation{ - tags: ["apps"], + tags: ["Applications"], summary: "Create an application", description: "Create a new application to obtain OAuth2 credentials", operationId: "AppController.create", @@ -45,8 +45,8 @@ def create_operation do def verify_credentials_operation do %Operation{ - tags: ["apps"], - summary: "Verify your app works", + tags: ["Applications"], + summary: "Verify the application works", description: "Confirm that the app's OAuth2 credentials work.", operationId: "AppController.verify_credentials", security: [%{"oAuth" => ["read"]}], diff --git a/lib/pleroma/web/api_spec/operations/chat_operation.ex b/lib/pleroma/web/api_spec/operations/chat_operation.ex index 560b81f17..23cb66392 100644 --- a/lib/pleroma/web/api_spec/operations/chat_operation.ex +++ b/lib/pleroma/web/api_spec/operations/chat_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.ChatOperation do @@ -20,7 +20,7 @@ def open_api_operation(action) do def mark_as_read_operation do %Operation{ - tags: ["chat"], + tags: ["Chats"], summary: "Mark all messages in the chat as read", operationId: "ChatController.mark_as_read", parameters: [Operation.parameter(:id, :path, :string, "The ID of the Chat")], @@ -43,8 +43,8 @@ def mark_as_read_operation do def mark_message_as_read_operation do %Operation{ - tags: ["chat"], - summary: "Mark one message in the chat as read", + tags: ["Chats"], + summary: "Mark a message as read", operationId: "ChatController.mark_message_as_read", parameters: [ Operation.parameter(:id, :path, :string, "The ID of the Chat"), @@ -68,8 +68,8 @@ def mark_message_as_read_operation do def show_operation do %Operation{ - tags: ["chat"], - summary: "Create a chat", + tags: ["Chats"], + summary: "Retrieve a chat", operationId: "ChatController.show", parameters: [ Operation.parameter( @@ -99,7 +99,7 @@ def show_operation do def create_operation do %Operation{ - tags: ["chat"], + tags: ["Chats"], summary: "Create a chat", operationId: "ChatController.create", parameters: [ @@ -130,9 +130,31 @@ def create_operation do def index_operation do %Operation{ - tags: ["chat"], - summary: "Get a list of chats that you participated in", + tags: ["Chats"], + summary: "Retrieve list of chats (unpaginated)", + deprecated: true, + description: + "Deprecated due to no support for pagination. Using [/api/v2/pleroma/chats](#operation/ChatController.index2) instead is recommended.", operationId: "ChatController.index", + parameters: [ + Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users") + ], + responses: %{ + 200 => Operation.response("The chats of the user", "application/json", chats_response()) + }, + security: [ + %{ + "oAuth" => ["read:chats"] + } + ] + } + end + + def index2_operation do + %Operation{ + tags: ["Chats"], + summary: "Retrieve list of chats", + operationId: "ChatController.index2", parameters: [ Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users") | pagination_params() @@ -150,8 +172,8 @@ def index_operation do def messages_operation do %Operation{ - tags: ["chat"], - summary: "Get the most recent messages of the chat", + tags: ["Chats"], + summary: "Retrieve chat's messages", operationId: "ChatController.messages", parameters: [Operation.parameter(:id, :path, :string, "The ID of the Chat")] ++ @@ -175,7 +197,7 @@ def messages_operation do def post_chat_message_operation do %Operation{ - tags: ["chat"], + tags: ["Chats"], summary: "Post a message to the chat", operationId: "ChatController.post_chat_message", parameters: [ @@ -202,8 +224,8 @@ def post_chat_message_operation do def delete_message_operation do %Operation{ - tags: ["chat"], - summary: "delete_message", + tags: ["Chats"], + summary: "Delete message", operationId: "ChatController.delete_message", parameters: [ Operation.parameter(:id, :path, :string, "The ID of the Chat"), @@ -236,7 +258,7 @@ def chats_response do "account" => %{ "pleroma" => %{ "is_admin" => false, - "confirmation_pending" => false, + "is_confirmed" => true, "hide_followers_count" => false, "is_moderator" => false, "hide_favorites" => true, diff --git a/lib/pleroma/web/api_spec/operations/conversation_operation.ex b/lib/pleroma/web/api_spec/operations/conversation_operation.ex index 475468893..17ed1af5e 100644 --- a/lib/pleroma/web/api_spec/operations/conversation_operation.ex +++ b/lib/pleroma/web/api_spec/operations/conversation_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.ConversationOperation do @@ -18,7 +18,7 @@ def open_api_operation(action) do def index_operation do %Operation{ tags: ["Conversations"], - summary: "Show conversation", + summary: "List of conversations", security: [%{"oAuth" => ["read:statuses"]}], operationId: "ConversationController.index", parameters: [ @@ -44,18 +44,33 @@ def index_operation do def mark_as_read_operation do %Operation{ tags: ["Conversations"], - summary: "Mark as read", + summary: "Mark conversation as read", operationId: "ConversationController.mark_as_read", - parameters: [ - Operation.parameter(:id, :path, :string, "Conversation ID", - example: "123", - required: true - ) - ], + parameters: [id_param()], security: [%{"oAuth" => ["write:conversations"]}], responses: %{ 200 => Operation.response("Conversation", "application/json", Conversation) } } end + + def delete_operation do + %Operation{ + tags: ["Conversations"], + summary: "Remove conversation", + operationId: "ConversationController.delete", + parameters: [id_param()], + security: [%{"oAuth" => ["write:conversations"]}], + responses: %{ + 200 => empty_object_response() + } + } + end + + def id_param do + Operation.parameter(:id, :path, :string, "Conversation ID", + example: "123", + required: true + ) + end end diff --git a/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex b/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex index 5ff263ceb..98da1a6de 100644 --- a/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex +++ b/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.CustomEmojiOperation do @@ -14,8 +14,8 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["custom_emojis"], - summary: "List custom custom emojis", + tags: ["Custom emojis"], + summary: "Retrieve a list of custom emojis", description: "Returns custom emojis that are available on the server.", operationId: "CustomEmojiController.index", responses: %{ diff --git a/lib/pleroma/web/api_spec/operations/domain_block_operation.ex b/lib/pleroma/web/api_spec/operations/domain_block_operation.ex index 1e0da8209..f124e7fe5 100644 --- a/lib/pleroma/web/api_spec/operations/domain_block_operation.ex +++ b/lib/pleroma/web/api_spec/operations/domain_block_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do @@ -14,9 +14,8 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["domain_blocks"], - summary: "Fetch domain blocks", - description: "View domains the user has blocked.", + tags: ["Domain blocks"], + summary: "Retrieve a list of blocked domains", security: [%{"oAuth" => ["follow", "read:blocks"]}], operationId: "DomainBlockController.index", responses: %{ @@ -34,7 +33,7 @@ def index_operation do # Supporting domain query parameter is deprecated in Mastodon API def create_operation do %Operation{ - tags: ["domain_blocks"], + tags: ["Domain blocks"], summary: "Block a domain", description: """ Block a domain to: @@ -55,7 +54,7 @@ def create_operation do # Supporting domain query parameter is deprecated in Mastodon API def delete_operation do %Operation{ - tags: ["domain_blocks"], + tags: ["Domain blocks"], summary: "Unblock a domain", description: "Remove a domain block, if it exists in the user's array of blocked domains.", operationId: "DomainBlockController.delete", diff --git a/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex b/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex index 9d0e39fc7..a7b306a30 100644 --- a/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex +++ b/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do @@ -17,7 +17,7 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["Emoji Reactions"], + tags: ["Emoji reactions"], summary: "Get an object of emoji to account mappings with accounts that reacted to the post", parameters: [ @@ -42,7 +42,7 @@ def index_operation do def create_operation do %Operation{ - tags: ["Emoji Reactions"], + tags: ["Emoji reactions"], summary: "React to a post with a unicode emoji", parameters: [ Operation.parameter(:id, :path, FlakeID, "Status ID", required: true), @@ -61,7 +61,7 @@ def create_operation do def delete_operation do %Operation{ - tags: ["Emoji Reactions"], + tags: ["Emoji reactions"], summary: "Remove a reaction to a post with a unicode emoji", parameters: [ Operation.parameter(:id, :path, FlakeID, "Status ID", required: true), @@ -78,7 +78,7 @@ def delete_operation do end defp array_of_reactions_response do - Operation.response("Array of Emoji Reactions", "application/json", %Schema{ + Operation.response("Array of Emoji reactions", "application/json", %Schema{ type: :array, items: emoji_reaction(), example: [emoji_reaction().example] diff --git a/lib/pleroma/web/api_spec/operations/filter_operation.ex b/lib/pleroma/web/api_spec/operations/filter_operation.ex index 31e576f99..5102921bc 100644 --- a/lib/pleroma/web/api_spec/operations/filter_operation.ex +++ b/lib/pleroma/web/api_spec/operations/filter_operation.ex @@ -1,11 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.FilterOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Helpers + alias Pleroma.Web.ApiSpec.Schemas.ApiError alias Pleroma.Web.ApiSpec.Schemas.BooleanLike def open_api_operation(action) do @@ -15,57 +16,64 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["apps"], - summary: "View all filters", + tags: ["Filters"], + summary: "All filters", operationId: "FilterController.index", security: [%{"oAuth" => ["read:filters"]}], responses: %{ - 200 => Operation.response("Filters", "application/json", array_of_filters()) + 200 => Operation.response("Filters", "application/json", array_of_filters()), + 403 => Operation.response("Error", "application/json", ApiError) } } end def create_operation do %Operation{ - tags: ["apps"], + tags: ["Filters"], summary: "Create a filter", operationId: "FilterController.create", requestBody: Helpers.request_body("Parameters", create_request(), required: true), security: [%{"oAuth" => ["write:filters"]}], - responses: %{200 => Operation.response("Filter", "application/json", filter())} + responses: %{ + 200 => Operation.response("Filter", "application/json", filter()), + 403 => Operation.response("Error", "application/json", ApiError) + } } end def show_operation do %Operation{ - tags: ["apps"], - summary: "View all filters", + tags: ["Filters"], + summary: "Filter", parameters: [id_param()], operationId: "FilterController.show", security: [%{"oAuth" => ["read:filters"]}], responses: %{ - 200 => Operation.response("Filter", "application/json", filter()) + 200 => Operation.response("Filter", "application/json", filter()), + 403 => Operation.response("Error", "application/json", ApiError), + 404 => Operation.response("Error", "application/json", ApiError) } } end def update_operation do %Operation{ - tags: ["apps"], + tags: ["Filters"], summary: "Update a filter", parameters: [id_param()], operationId: "FilterController.update", requestBody: Helpers.request_body("Parameters", update_request(), required: true), security: [%{"oAuth" => ["write:filters"]}], responses: %{ - 200 => Operation.response("Filter", "application/json", filter()) + 200 => Operation.response("Filter", "application/json", filter()), + 403 => Operation.response("Error", "application/json", ApiError) } } end def delete_operation do %Operation{ - tags: ["apps"], + tags: ["Filters"], summary: "Remove a filter", parameters: [id_param()], operationId: "FilterController.delete", @@ -75,7 +83,8 @@ def delete_operation do Operation.response("Filter", "application/json", %Schema{ type: :object, description: "Empty object" - }) + }), + 403 => Operation.response("Error", "application/json", ApiError) } } end @@ -210,15 +219,13 @@ defp update_request do nullable: true, description: "Consider word boundaries?", default: true + }, + expires_in: %Schema{ + nullable: true, + type: :integer, + description: + "Number of seconds from now the filter should expire. Otherwise, null for a filter that doesn't expire." } - # TODO: probably should implement filter expiration - # expires_in: %Schema{ - # type: :string, - # format: :"date-time", - # description: - # "ISO 8601 Datetime for when the filter expires. Otherwise, - # null for a filter that doesn't expire." - # } }, required: [:phrase, :context], example: %{ diff --git a/lib/pleroma/web/api_spec/operations/follow_request_operation.ex b/lib/pleroma/web/api_spec/operations/follow_request_operation.ex index ac4aee6da..784019699 100644 --- a/lib/pleroma/web/api_spec/operations/follow_request_operation.ex +++ b/lib/pleroma/web/api_spec/operations/follow_request_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.FollowRequestOperation do @@ -15,8 +15,8 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["Follow Requests"], - summary: "Pending Follows", + tags: ["Follow requests"], + summary: "Retrieve follow requests", security: [%{"oAuth" => ["read:follows", "follow"]}], operationId: "FollowRequestController.index", responses: %{ @@ -32,8 +32,8 @@ def index_operation do def authorize_operation do %Operation{ - tags: ["Follow Requests"], - summary: "Accept Follow", + tags: ["Follow requests"], + summary: "Accept follow request", operationId: "FollowRequestController.authorize", parameters: [id_param()], security: [%{"oAuth" => ["follow", "write:follows"]}], @@ -45,8 +45,8 @@ def authorize_operation do def reject_operation do %Operation{ - tags: ["Follow Requests"], - summary: "Reject Follow", + tags: ["Follow requests"], + summary: "Reject follow request", operationId: "FollowRequestController.reject", parameters: [id_param()], security: [%{"oAuth" => ["follow", "write:follows"]}], diff --git a/lib/pleroma/web/api_spec/operations/instance_operation.ex b/lib/pleroma/web/api_spec/operations/instance_operation.ex index bf39ae643..9384acc32 100644 --- a/lib/pleroma/web/api_spec/operations/instance_operation.ex +++ b/lib/pleroma/web/api_spec/operations/instance_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.InstanceOperation do @@ -14,7 +14,7 @@ def open_api_operation(action) do def show_operation do %Operation{ tags: ["Instance"], - summary: "Fetch instance", + summary: "Retrieve instance information", description: "Information about the server", operationId: "InstanceController.show", responses: %{ @@ -26,7 +26,7 @@ def show_operation do def peers_operation do %Operation{ tags: ["Instance"], - summary: "List of known hosts", + summary: "Retrieve list of known instances", operationId: "InstanceController.peers", responses: %{ 200 => Operation.response("Array of domains", "application/json", array_of_domains()) diff --git a/lib/pleroma/web/api_spec/operations/list_operation.ex b/lib/pleroma/web/api_spec/operations/list_operation.ex index f6e73968a..8a6e92b99 100644 --- a/lib/pleroma/web/api_spec/operations/list_operation.ex +++ b/lib/pleroma/web/api_spec/operations/list_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.ListOperation do @@ -20,7 +20,7 @@ def open_api_operation(action) do def index_operation do %Operation{ tags: ["Lists"], - summary: "Show user's lists", + summary: "Retrieve a list of lists", description: "Fetch all lists that the user owns", security: [%{"oAuth" => ["read:lists"]}], operationId: "ListController.index", @@ -33,7 +33,7 @@ def index_operation do def create_operation do %Operation{ tags: ["Lists"], - summary: "Create a list", + summary: "Create a list", description: "Fetch the list with the given ID. Used for verifying the title of a list.", operationId: "ListController.create", requestBody: create_update_request(), @@ -49,7 +49,7 @@ def create_operation do def show_operation do %Operation{ tags: ["Lists"], - summary: "Show a single list", + summary: "Retrieve a list", description: "Fetch the list with the given ID. Used for verifying the title of a list.", operationId: "ListController.show", parameters: [id_param()], @@ -93,7 +93,7 @@ def delete_operation do def list_accounts_operation do %Operation{ tags: ["Lists"], - summary: "View accounts in list", + summary: "Retrieve accounts in list", operationId: "ListController.list_accounts", parameters: [id_param()], security: [%{"oAuth" => ["read:lists"]}], diff --git a/lib/pleroma/web/api_spec/operations/marker_operation.ex b/lib/pleroma/web/api_spec/operations/marker_operation.ex index 714ef1f99..c5ff5984b 100644 --- a/lib/pleroma/web/api_spec/operations/marker_operation.ex +++ b/lib/pleroma/web/api_spec/operations/marker_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.MarkerOperation do diff --git a/lib/pleroma/web/api_spec/operations/media_operation.ex b/lib/pleroma/web/api_spec/operations/media_operation.ex index d9c3c42db..85aa14869 100644 --- a/lib/pleroma/web/api_spec/operations/media_operation.ex +++ b/lib/pleroma/web/api_spec/operations/media_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.MediaOperation do @@ -16,7 +16,7 @@ def open_api_operation(action) do def create_operation do %Operation{ - tags: ["media"], + tags: ["Media attachments"], summary: "Upload media as attachment", description: "Creates an attachment to be used with a new status.", operationId: "MediaController.create", @@ -56,8 +56,8 @@ defp create_request do def update_operation do %Operation{ - tags: ["media"], - summary: "Upload media as attachment", + tags: ["Media attachments"], + summary: "Update attachment", description: "Creates an attachment to be used with a new status.", operationId: "MediaController.update", security: [%{"oAuth" => ["write:media"]}], @@ -97,8 +97,8 @@ defp update_request do def show_operation do %Operation{ - tags: ["media"], - summary: "Show Uploaded media attachment", + tags: ["Media attachments"], + summary: "Attachment", operationId: "MediaController.show", parameters: [id_param()], security: [%{"oAuth" => ["read:media"]}], @@ -112,8 +112,8 @@ def show_operation do def create2_operation do %Operation{ - tags: ["media"], - summary: "Upload media as attachment", + tags: ["Media attachments"], + summary: "Upload media as attachment (v2)", description: "Creates an attachment to be used with a new status.", operationId: "MediaController.create2", security: [%{"oAuth" => ["write:media"]}], diff --git a/lib/pleroma/web/api_spec/operations/notification_operation.ex b/lib/pleroma/web/api_spec/operations/notification_operation.ex index 264a530d2..ec88eabe1 100644 --- a/lib/pleroma/web/api_spec/operations/notification_operation.ex +++ b/lib/pleroma/web/api_spec/operations/notification_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.NotificationOperation do @@ -22,7 +22,7 @@ def open_api_operation(action) do def index_operation do %Operation{ tags: ["Notifications"], - summary: "Get all notifications", + summary: "Retrieve a list of notifications", description: "Notifications concerning the user. This API returns Link headers containing links to the next/previous page. However, the links can also be constructed dynamically using query params and `id` values.", operationId: "NotificationController.index", @@ -74,7 +74,7 @@ def index_operation do def show_operation do %Operation{ tags: ["Notifications"], - summary: "Get a single notification", + summary: "Retrieve a notification", description: "View information about a notification with a given ID.", operationId: "NotificationController.show", security: [%{"oAuth" => ["read:notifications"]}], @@ -99,7 +99,7 @@ def clear_operation do def dismiss_operation do %Operation{ tags: ["Notifications"], - summary: "Dismiss a single notification", + summary: "Dismiss a notification", description: "Clear a single notification from the server.", operationId: "NotificationController.dismiss", parameters: [id_param()], diff --git a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex index 97836b2eb..ad49f6426 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do @@ -18,8 +18,9 @@ def open_api_operation(action) do def confirmation_resend_operation do %Operation{ - tags: ["Accounts"], - summary: "Resend confirmation email. Expects `email` or `nickname`", + tags: ["Account credentials"], + summary: "Resend confirmation email", + description: "Expects `email` or `nickname`.", operationId: "PleromaAPI.AccountController.confirmation_resend", parameters: [ Operation.parameter(:email, :query, :string, "Email of that needs to be verified", @@ -41,8 +42,10 @@ def confirmation_resend_operation do def favourites_operation do %Operation{ - tags: ["Accounts"], - summary: "Returns favorites timeline of any user", + tags: ["Retrieve account information"], + summary: "Favorites", + description: + "Only returns data if the user has opted into sharing it. See `hide_favorites` in [Update account credentials](#operation/AccountController.update_credentials).", operationId: "PleromaAPI.AccountController.favourites", parameters: [id_param() | pagination_params()], security: [%{"oAuth" => ["read:favourites"]}], @@ -61,8 +64,9 @@ def favourites_operation do def subscribe_operation do %Operation{ - tags: ["Accounts"], - summary: "Subscribe to receive notifications for all statuses posted by a user", + tags: ["Account actions"], + summary: "Subscribe", + description: "Receive notifications for all statuses posted by the account.", operationId: "PleromaAPI.AccountController.subscribe", parameters: [id_param()], security: [%{"oAuth" => ["follow", "write:follows"]}], @@ -75,8 +79,9 @@ def subscribe_operation do def unsubscribe_operation do %Operation{ - tags: ["Accounts"], - summary: "Unsubscribe to stop receiving notifications from user statuses", + tags: ["Account actions"], + summary: "Unsubscribe", + description: "Stop receiving notifications for all statuses posted by the account.", operationId: "PleromaAPI.AccountController.unsubscribe", parameters: [id_param()], security: [%{"oAuth" => ["follow", "write:follows"]}], diff --git a/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex index 6993794db..c78e9780f 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation do diff --git a/lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex index e885eab20..12fb8ed36 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaConversationOperation do @@ -19,7 +19,7 @@ def open_api_operation(action) do def show_operation do %Operation{ tags: ["Conversations"], - summary: "The conversation with the given ID", + summary: "Conversation", parameters: [ Operation.parameter(:id, :path, :string, "Conversation ID", example: "123", @@ -37,7 +37,7 @@ def show_operation do def statuses_operation do %Operation{ tags: ["Conversations"], - summary: "Timeline for a given conversation", + summary: "Timeline for conversation", parameters: [ Operation.parameter(:id, :path, :string, "Conversation ID", example: "123", @@ -61,7 +61,8 @@ def statuses_operation do def update_operation do %Operation{ tags: ["Conversations"], - summary: "Update a conversation. Used to change the set of recipients.", + summary: "Update conversation", + description: "Change set of recipients for the conversation.", parameters: [ Operation.parameter(:id, :path, :string, "Conversation ID", example: "123", @@ -86,7 +87,7 @@ def update_operation do def mark_as_read_operation do %Operation{ tags: ["Conversations"], - summary: "Marks all user's conversations as read", + summary: "Marks all conversations as read", security: [%{"oAuth" => ["write:conversations"]}], operationId: "PleromaAPI.ConversationController.mark_as_read", responses: %{ diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex index a56641426..8c76096b5 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do @@ -16,10 +16,10 @@ def open_api_operation(action) do def create_operation do %Operation{ - tags: ["Emoji Packs"], + tags: ["Emoji pack administration"], summary: "Add new file to the pack", operationId: "PleromaAPI.EmojiPackController.add_file", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], requestBody: request_body("Parameters", create_request(), required: true), parameters: [name_param()], responses: %{ @@ -27,7 +27,8 @@ def create_operation do 422 => Operation.response("Unprocessable Entity", "application/json", ApiError), 404 => Operation.response("Not Found", "application/json", ApiError), 400 => Operation.response("Bad Request", "application/json", ApiError), - 409 => Operation.response("Conflict", "application/json", ApiError) + 409 => Operation.response("Conflict", "application/json", ApiError), + 500 => Operation.response("Error", "application/json", ApiError) } } end @@ -61,10 +62,10 @@ defp create_request do def update_operation do %Operation{ - tags: ["Emoji Packs"], + tags: ["Emoji pack administration"], summary: "Add new file to the pack", operationId: "PleromaAPI.EmojiPackController.update_file", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], requestBody: request_body("Parameters", update_request(), required: true), parameters: [name_param()], responses: %{ @@ -105,10 +106,10 @@ defp update_request do def delete_operation do %Operation{ - tags: ["Emoji Packs"], + tags: ["Emoji pack administration"], summary: "Delete emoji file from pack", operationId: "PleromaAPI.EmojiPackController.delete_file", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], parameters: [ name_param(), Operation.parameter(:shortcode, :query, :string, "File shortcode", diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex index 79f52dcb3..49247d9b6 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do @@ -16,9 +16,9 @@ def open_api_operation(action) do def remote_operation do %Operation{ - tags: ["Emoji Packs"], + tags: ["Emoji pack administration"], summary: "Make request to another instance for emoji packs list", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], parameters: [ url_param(), Operation.parameter( @@ -44,7 +44,7 @@ def remote_operation do def index_operation do %Operation{ - tags: ["Emoji Packs"], + tags: ["Emoji packs"], summary: "Lists local custom emoji packs", operationId: "PleromaAPI.EmojiPackController.index", parameters: [ @@ -69,7 +69,7 @@ def index_operation do def show_operation do %Operation{ - tags: ["Emoji Packs"], + tags: ["Emoji packs"], summary: "Show emoji pack", operationId: "PleromaAPI.EmojiPackController.show", parameters: [ @@ -97,7 +97,7 @@ def show_operation do def archive_operation do %Operation{ - tags: ["Emoji Packs"], + tags: ["Emoji packs"], summary: "Requests a local pack archive from the instance", operationId: "PleromaAPI.EmojiPackController.archive", parameters: [name_param()], @@ -115,10 +115,10 @@ def archive_operation do def download_operation do %Operation{ - tags: ["Emoji Packs"], + tags: ["Emoji pack administration"], summary: "Download pack from another instance", operationId: "PleromaAPI.EmojiPackController.download", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], requestBody: request_body("Parameters", download_request(), required: true), responses: %{ 200 => ok_response(), @@ -145,10 +145,10 @@ defp download_request do def create_operation do %Operation{ - tags: ["Emoji Packs"], + tags: ["Emoji pack administration"], summary: "Create an empty pack", operationId: "PleromaAPI.EmojiPackController.create", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], parameters: [name_param()], responses: %{ 200 => ok_response(), @@ -161,40 +161,42 @@ def create_operation do def delete_operation do %Operation{ - tags: ["Emoji Packs"], + tags: ["Emoji pack administration"], summary: "Delete a custom emoji pack", operationId: "PleromaAPI.EmojiPackController.delete", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], parameters: [name_param()], responses: %{ 200 => ok_response(), 400 => Operation.response("Bad Request", "application/json", ApiError), - 404 => Operation.response("Not Found", "application/json", ApiError) + 404 => Operation.response("Not Found", "application/json", ApiError), + 500 => Operation.response("Error", "application/json", ApiError) } } end def update_operation do %Operation{ - tags: ["Emoji Packs"], + tags: ["Emoji pack administration"], summary: "Updates (replaces) pack metadata", operationId: "PleromaAPI.EmojiPackController.update", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], requestBody: request_body("Parameters", update_request(), required: true), parameters: [name_param()], responses: %{ 200 => Operation.response("Metadata", "application/json", metadata()), - 400 => Operation.response("Bad Request", "application/json", ApiError) + 400 => Operation.response("Bad Request", "application/json", ApiError), + 500 => Operation.response("Error", "application/json", ApiError) } } end def import_from_filesystem_operation do %Operation{ - tags: ["Emoji Packs"], + tags: ["Emoji pack administration"], summary: "Imports packs from filesystem", operationId: "PleromaAPI.EmojiPackController.import", - security: [%{"oAuth" => ["write"]}], + security: [%{"oAuth" => ["admin:write"]}], responses: %{ 200 => Operation.response("Array of imported pack names", "application/json", %Schema{ diff --git a/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex index 2c455b0df..612113147 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaInstancesOperation do @@ -13,8 +13,8 @@ def open_api_operation(action) do def show_operation do %Operation{ - tags: ["PleromaInstances"], - summary: "Instances federation status", + tags: ["Instance"], + summary: "Retrieve federation status", description: "Information about instances deemed unreachable by the server", operationId: "PleromaInstances.show", responses: %{ diff --git a/lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex index 8c5f37ea6..6191cb97d 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaMascotOperation do @@ -17,7 +17,7 @@ def open_api_operation(action) do def show_operation do %Operation{ tags: ["Mascot"], - summary: "Gets user mascot image", + summary: "Retrieve mascot", security: [%{"oAuth" => ["read:accounts"]}], operationId: "PleromaAPI.MascotController.show", responses: %{ @@ -29,7 +29,7 @@ def show_operation do def update_operation do %Operation{ tags: ["Mascot"], - summary: "Set/clear user avatar image", + summary: "Set or clear mascot", description: "Behaves exactly the same as `POST /api/v1/upload`. Can only accept images - any attempt to upload non-image files will be met with `HTTP 415 Unsupported Media Type`.", operationId: "PleromaAPI.MascotController.update", diff --git a/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex index b0c8db863..1dda39240 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaNotificationOperation do @@ -18,7 +18,8 @@ def open_api_operation(action) do def mark_as_read_operation do %Operation{ tags: ["Notifications"], - summary: "Mark notifications as read. Query parameters are mutually exclusive.", + summary: "Mark notifications as read", + description: "Query parameters are mutually exclusive.", requestBody: request_body("Parameters", %Schema{ type: :object, @@ -32,7 +33,7 @@ def mark_as_read_operation do responses: %{ 200 => Operation.response( - "A Notification or array of Motifications", + "A Notification or array of Notifications", "application/json", %Schema{ anyOf: [ diff --git a/lib/pleroma/web/api_spec/operations/pleroma_report_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_report_operation.ex new file mode 100644 index 000000000..ee8870dc2 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/pleroma_report_operation.ex @@ -0,0 +1,97 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.PleromaReportOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Admin.ReportOperation + alias Pleroma.Web.ApiSpec.Schemas.Account + alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.FlakeID + alias Pleroma.Web.ApiSpec.Schemas.Status + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %Operation{ + tags: ["Reports"], + summary: "Get a list of your own reports", + operationId: "PleromaAPI.ReportController.index", + security: [%{"oAuth" => ["read:reports"]}], + parameters: [ + Operation.parameter( + :state, + :query, + ReportOperation.report_state(), + "Filter by report state" + ), + Operation.parameter( + :limit, + :query, + %Schema{type: :integer}, + "The number of records to retrieve" + ), + Operation.parameter( + :page, + :query, + %Schema{type: :integer, default: 1}, + "Page number" + ), + Operation.parameter( + :page_size, + :query, + %Schema{type: :integer, default: 50}, + "Number number of log entries per page" + ) + ], + responses: %{ + 200 => + Operation.response("Response", "application/json", %Schema{ + type: :object, + properties: %{ + total: %Schema{type: :integer}, + reports: %Schema{ + type: :array, + items: report() + } + } + }), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def show_operation do + %Operation{ + tags: ["Reports"], + summary: "Get an individual report", + operationId: "PleromaAPI.ReportController.show", + parameters: [ReportOperation.id_param()], + security: [%{"oAuth" => ["read:reports"]}], + responses: %{ + 200 => Operation.response("Report", "application/json", report()), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + # Copied from ReportOperation.report with removing notes + defp report do + %Schema{ + type: :object, + properties: %{ + id: FlakeID, + state: ReportOperation.report_state(), + account: Account, + actor: Account, + content: %Schema{type: :string}, + created_at: %Schema{type: :string, format: :"date-time"}, + statuses: %Schema{type: :array, items: Status} + } + } + end +end diff --git a/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex index 85a22aa0b..6a909fc85 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do diff --git a/lib/pleroma/web/api_spec/operations/poll_operation.ex b/lib/pleroma/web/api_spec/operations/poll_operation.ex index e15c7dc95..0d1c8d099 100644 --- a/lib/pleroma/web/api_spec/operations/poll_operation.ex +++ b/lib/pleroma/web/api_spec/operations/poll_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.PollOperation do diff --git a/lib/pleroma/web/api_spec/operations/report_operation.ex b/lib/pleroma/web/api_spec/operations/report_operation.ex index b9b4c4f79..b744efa60 100644 --- a/lib/pleroma/web/api_spec/operations/report_operation.ex +++ b/lib/pleroma/web/api_spec/operations/report_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.ReportOperation do @@ -16,7 +16,7 @@ def open_api_operation(action) do def create_operation do %Operation{ - tags: ["reports"], + tags: ["Reports"], summary: "File a report", description: "Report problematic users to your moderators", operationId: "ReportController.create", diff --git a/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex b/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex index fe675a923..b9c5b35c1 100644 --- a/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex +++ b/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.ScheduledActivityOperation do @@ -18,7 +18,7 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["Scheduled Statuses"], + tags: ["Scheduled statuses"], summary: "View scheduled statuses", security: [%{"oAuth" => ["read:statuses"]}], parameters: pagination_params(), @@ -35,7 +35,7 @@ def index_operation do def show_operation do %Operation{ - tags: ["Scheduled Statuses"], + tags: ["Scheduled statuses"], summary: "View a single scheduled status", security: [%{"oAuth" => ["read:statuses"]}], parameters: [id_param()], @@ -49,7 +49,7 @@ def show_operation do def update_operation do %Operation{ - tags: ["Scheduled Statuses"], + tags: ["Scheduled statuses"], summary: "Schedule a status", operationId: "ScheduledActivity.update", security: [%{"oAuth" => ["write:statuses"]}], @@ -75,7 +75,7 @@ def update_operation do def delete_operation do %Operation{ - tags: ["Scheduled Statuses"], + tags: ["Scheduled statuses"], summary: "Cancel a scheduled status", security: [%{"oAuth" => ["write:statuses"]}], parameters: [id_param()], diff --git a/lib/pleroma/web/api_spec/operations/search_operation.ex b/lib/pleroma/web/api_spec/operations/search_operation.ex index 169c36d87..ff4fd0027 100644 --- a/lib/pleroma/web/api_spec/operations/search_operation.ex +++ b/lib/pleroma/web/api_spec/operations/search_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.SearchOperation do diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index 4ab918d83..802fbef3e 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.StatusOperation do @@ -22,8 +22,8 @@ def open_api_operation(action) do def index_operation do %Operation{ - tags: ["Statuses"], - summary: "Get multiple statuses by IDs", + tags: ["Retrieve status information"], + summary: "Multiple statuses", security: [%{"oAuth" => ["read:statuses"]}], parameters: [ Operation.parameter( @@ -48,7 +48,7 @@ def index_operation do def create_operation do %Operation{ - tags: ["Statuses"], + tags: ["Status actions"], summary: "Publish new status", security: [%{"oAuth" => ["write:statuses"]}], description: "Post a new status", @@ -59,7 +59,7 @@ def create_operation do Operation.response( "Status. When `scheduled_at` is present, ScheduledStatus is returned instead", "application/json", - %Schema{oneOf: [Status, ScheduledStatus]} + %Schema{anyOf: [Status, ScheduledStatus]} ), 422 => Operation.response("Bad Request / MRF Rejection", "application/json", ApiError) } @@ -68,8 +68,8 @@ def create_operation do def show_operation do %Operation{ - tags: ["Statuses"], - summary: "View specific status", + tags: ["Retrieve status information"], + summary: "Status", description: "View information about a status", operationId: "StatusController.show", security: [%{"oAuth" => ["read:statuses"]}], @@ -91,8 +91,8 @@ def show_operation do def delete_operation do %Operation{ - tags: ["Statuses"], - summary: "Delete status", + tags: ["Status actions"], + summary: "Delete", security: [%{"oAuth" => ["write:statuses"]}], description: "Delete one of your own statuses", operationId: "StatusController.delete", @@ -107,8 +107,8 @@ def delete_operation do def reblog_operation do %Operation{ - tags: ["Statuses"], - summary: "Boost", + tags: ["Status actions"], + summary: "Reblog", security: [%{"oAuth" => ["write:statuses"]}], description: "Share a status", operationId: "StatusController.reblog", @@ -117,7 +117,7 @@ def reblog_operation do request_body("Parameters", %Schema{ type: :object, properties: %{ - visibility: %Schema{allOf: [VisibilityScope], default: "public"} + visibility: %Schema{allOf: [VisibilityScope]} } }), responses: %{ @@ -129,8 +129,8 @@ def reblog_operation do def unreblog_operation do %Operation{ - tags: ["Statuses"], - summary: "Undo boost", + tags: ["Status actions"], + summary: "Undo reblog", security: [%{"oAuth" => ["write:statuses"]}], description: "Undo a reshare of a status", operationId: "StatusController.unreblog", @@ -144,7 +144,7 @@ def unreblog_operation do def favourite_operation do %Operation{ - tags: ["Statuses"], + tags: ["Status actions"], summary: "Favourite", security: [%{"oAuth" => ["write:favourites"]}], description: "Add a status to your favourites list", @@ -159,7 +159,7 @@ def favourite_operation do def unfavourite_operation do %Operation{ - tags: ["Statuses"], + tags: ["Status actions"], summary: "Undo favourite", security: [%{"oAuth" => ["write:favourites"]}], description: "Remove a status from your favourites list", @@ -174,7 +174,7 @@ def unfavourite_operation do def pin_operation do %Operation{ - tags: ["Statuses"], + tags: ["Status actions"], summary: "Pin to profile", security: [%{"oAuth" => ["write:accounts"]}], description: "Feature one of your own public statuses at the top of your profile", @@ -182,29 +182,71 @@ def pin_operation do parameters: [id_param()], responses: %{ 200 => status_response(), - 400 => Operation.response("Error", "application/json", ApiError) + 400 => + Operation.response("Bad Request", "application/json", %Schema{ + allOf: [ApiError], + title: "Unprocessable Entity", + example: %{ + "error" => "You have already pinned the maximum number of statuses" + } + }), + 404 => + Operation.response("Not found", "application/json", %Schema{ + allOf: [ApiError], + title: "Unprocessable Entity", + example: %{ + "error" => "Record not found" + } + }), + 422 => + Operation.response( + "Unprocessable Entity", + "application/json", + %Schema{ + allOf: [ApiError], + title: "Unprocessable Entity", + example: %{ + "error" => "Someone else's status cannot be pinned" + } + } + ) } } end def unpin_operation do %Operation{ - tags: ["Statuses"], - summary: "Unpin to profile", + tags: ["Status actions"], + summary: "Unpin from profile", security: [%{"oAuth" => ["write:accounts"]}], description: "Unfeature a status from the top of your profile", operationId: "StatusController.unpin", parameters: [id_param()], responses: %{ 200 => status_response(), - 400 => Operation.response("Error", "application/json", ApiError) + 400 => + Operation.response("Bad Request", "application/json", %Schema{ + allOf: [ApiError], + title: "Unprocessable Entity", + example: %{ + "error" => "You have already pinned the maximum number of statuses" + } + }), + 404 => + Operation.response("Not found", "application/json", %Schema{ + allOf: [ApiError], + title: "Unprocessable Entity", + example: %{ + "error" => "Record not found" + } + }) } } end def bookmark_operation do %Operation{ - tags: ["Statuses"], + tags: ["Status actions"], summary: "Bookmark", security: [%{"oAuth" => ["write:bookmarks"]}], description: "Privately bookmark a status", @@ -218,7 +260,7 @@ def bookmark_operation do def unbookmark_operation do %Operation{ - tags: ["Statuses"], + tags: ["Status actions"], summary: "Undo bookmark", security: [%{"oAuth" => ["write:bookmarks"]}], description: "Remove a status from your private bookmarks", @@ -232,7 +274,7 @@ def unbookmark_operation do def mute_conversation_operation do %Operation{ - tags: ["Statuses"], + tags: ["Status actions"], summary: "Mute conversation", security: [%{"oAuth" => ["write:mutes"]}], description: "Do not receive notifications for the thread that this status is part of.", @@ -267,7 +309,7 @@ def mute_conversation_operation do def unmute_conversation_operation do %Operation{ - tags: ["Statuses"], + tags: ["Status actions"], summary: "Unmute conversation", security: [%{"oAuth" => ["write:mutes"]}], description: @@ -283,7 +325,7 @@ def unmute_conversation_operation do def card_operation do %Operation{ - tags: ["Statuses"], + tags: ["Retrieve status information"], deprecated: true, summary: "Preview card", description: "Deprecated in favor of card property inlined on Status entity", @@ -311,7 +353,7 @@ def card_operation do def favourited_by_operation do %Operation{ - tags: ["Statuses"], + tags: ["Retrieve status information"], summary: "Favourited by", description: "View who favourited a given status", operationId: "StatusController.favourited_by", @@ -331,9 +373,9 @@ def favourited_by_operation do def reblogged_by_operation do %Operation{ - tags: ["Statuses"], - summary: "Boosted by", - description: "View who boosted a given status", + tags: ["Retrieve status information"], + summary: "Reblogged by", + description: "View who reblogged a given status", operationId: "StatusController.reblogged_by", security: [%{"oAuth" => ["read:accounts"]}], parameters: [id_param()], @@ -351,7 +393,7 @@ def reblogged_by_operation do def context_operation do %Operation{ - tags: ["Statuses"], + tags: ["Retrieve status information"], summary: "Parent and child statuses", description: "View statuses above and below this status in the thread", operationId: "StatusController.context", @@ -365,7 +407,7 @@ def context_operation do def favourites_operation do %Operation{ - tags: ["Statuses"], + tags: ["Timelines"], summary: "Favourited statuses", description: "Statuses the user has favourited. Please note that you have to use the link headers to paginate this. You can not build the query parameters yourself.", @@ -380,7 +422,7 @@ def favourites_operation do def bookmarks_operation do %Operation{ - tags: ["Statuses"], + tags: ["Timelines"], summary: "Bookmarked statuses", description: "Statuses the user has bookmarked", operationId: "StatusController.bookmarks", @@ -413,34 +455,7 @@ defp create_request do items: %Schema{type: :string}, description: "Array of Attachment ids to be attached as media." }, - poll: %Schema{ - nullable: true, - type: :object, - required: [:options], - properties: %{ - options: %Schema{ - type: :array, - items: %Schema{type: :string}, - description: "Array of possible answers. Must be provided with `poll[expires_in]`." - }, - expires_in: %Schema{ - type: :integer, - nullable: true, - description: - "Duration the poll should be open, in seconds. Must be provided with `poll[options]`" - }, - multiple: %Schema{ - allOf: [BooleanLike], - nullable: true, - description: "Allow multiple choices?" - }, - hide_totals: %Schema{ - allOf: [BooleanLike], - nullable: true, - description: "Hide vote counts until the poll ends?" - } - } - }, + poll: poll_params(), in_reply_to_id: %Schema{ nullable: true, allOf: [FlakeID], @@ -522,6 +537,37 @@ defp create_request do } end + def poll_params do + %Schema{ + nullable: true, + type: :object, + required: [:options, :expires_in], + properties: %{ + options: %Schema{ + type: :array, + items: %Schema{type: :string}, + description: "Array of possible answers. Must be provided with `poll[expires_in]`." + }, + expires_in: %Schema{ + type: :integer, + nullable: true, + description: + "Duration the poll should be open, in seconds. Must be provided with `poll[options]`" + }, + multiple: %Schema{ + allOf: [BooleanLike], + nullable: true, + description: "Allow multiple choices?" + }, + hide_totals: %Schema{ + allOf: [BooleanLike], + nullable: true, + description: "Hide vote counts until the poll ends?" + } + } + } + end + def id_param do Operation.parameter(:id, :path, FlakeID, "Status ID", example: "9umDrYheeY451cQnEe", diff --git a/lib/pleroma/web/api_spec/operations/subscription_operation.ex b/lib/pleroma/web/api_spec/operations/subscription_operation.ex index 67c7ea8f3..60a7fb3b0 100644 --- a/lib/pleroma/web/api_spec/operations/subscription_operation.ex +++ b/lib/pleroma/web/api_spec/operations/subscription_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.SubscriptionOperation do @@ -17,7 +17,7 @@ def open_api_operation(action) do def create_operation do %Operation{ - tags: ["Push Subscriptions"], + tags: ["Push subscriptions"], summary: "Subscribe to push notifications", description: "Add a Web Push API subscription to receive notifications. Each access token can have one push subscription. If you create a new subscription, the old subscription is deleted.", @@ -25,7 +25,7 @@ def create_operation do security: [%{"oAuth" => ["push"]}], requestBody: Helpers.request_body("Parameters", create_request(), required: true), responses: %{ - 200 => Operation.response("Push Subscription", "application/json", PushSubscription), + 200 => Operation.response("Push subscription", "application/json", PushSubscription), 400 => Operation.response("Error", "application/json", ApiError), 403 => Operation.response("Error", "application/json", ApiError) } @@ -34,13 +34,13 @@ def create_operation do def show_operation do %Operation{ - tags: ["Push Subscriptions"], + tags: ["Push subscriptions"], summary: "Get current subscription", description: "View the PushSubscription currently associated with this access token.", operationId: "SubscriptionController.show", security: [%{"oAuth" => ["push"]}], responses: %{ - 200 => Operation.response("Push Subscription", "application/json", PushSubscription), + 200 => Operation.response("Push subscription", "application/json", PushSubscription), 403 => Operation.response("Error", "application/json", ApiError), 404 => Operation.response("Error", "application/json", ApiError) } @@ -49,7 +49,7 @@ def show_operation do def update_operation do %Operation{ - tags: ["Push Subscriptions"], + tags: ["Push subscriptions"], summary: "Change types of notifications", description: "Updates the current push subscription. Only the data part can be updated. To change fundamentals, a new subscription must be created instead.", @@ -57,7 +57,7 @@ def update_operation do security: [%{"oAuth" => ["push"]}], requestBody: Helpers.request_body("Parameters", update_request(), required: true), responses: %{ - 200 => Operation.response("Push Subscription", "application/json", PushSubscription), + 200 => Operation.response("Push subscription", "application/json", PushSubscription), 403 => Operation.response("Error", "application/json", ApiError) } } @@ -65,7 +65,7 @@ def update_operation do def delete_operation do %Operation{ - tags: ["Push Subscriptions"], + tags: ["Push subscriptions"], summary: "Remove current subscription", description: "Removes the current Web Push API subscription.", operationId: "SubscriptionController.delete", diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex index 95720df9f..cae18c758 100644 --- a/lib/pleroma/web/api_spec/operations/timeline_operation.ex +++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.TimelineOperation do @@ -25,6 +25,8 @@ def home_operation do security: [%{"oAuth" => ["read:statuses"]}], parameters: [ local_param(), + remote_param(), + only_media_param(), with_muted_param(), exclude_visibilities_param(), reply_visibility_param() | pagination_params() @@ -41,8 +43,7 @@ def direct_operation do tags: ["Timelines"], summary: "Direct timeline", description: - "View statuses with a “direct” privacy, from your account or in your notifications", - deprecated: true, + "View statuses with a “direct” scope addressed to the account. Using this endpoint is discouraged, please use [conversations](#tag/Conversations) or [chats](#tag/Chats).", parameters: [with_muted_param() | pagination_params()], security: [%{"oAuth" => ["read:statuses"]}], operationId: "TimelineController.direct", @@ -61,6 +62,7 @@ def public_operation do local_param(), instance_param(), only_media_param(), + remote_param(), with_muted_param(), exclude_visibilities_param(), reply_visibility_param() | pagination_params() @@ -107,6 +109,7 @@ def hashtag_operation do ), local_param(), only_media_param(), + remote_param(), with_muted_param(), exclude_visibilities_param() | pagination_params() ], @@ -132,6 +135,9 @@ def list_operation do required: true ), with_muted_param(), + local_param(), + remote_param(), + only_media_param(), exclude_visibilities_param() | pagination_params() ], operationId: "TimelineController.list", @@ -198,4 +204,13 @@ defp only_media_param do "Show only statuses with media attached?" ) end + + defp remote_param do + Operation.parameter( + :remote, + :query, + %Schema{allOf: [BooleanLike], default: false}, + "Show only remote statuses?" + ) + end end diff --git a/lib/pleroma/web/api_spec/operations/user_import_operation.ex b/lib/pleroma/web/api_spec/operations/user_import_operation.ex index a50314fb7..6292e2004 100644 --- a/lib/pleroma/web/api_spec/operations/user_import_operation.ex +++ b/lib/pleroma/web/api_spec/operations/user_import_operation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.UserImportOperation do @@ -17,8 +17,8 @@ def open_api_operation(action) do def follow_operation do %Operation{ - tags: ["follow_import"], - summary: "Imports your follows.", + tags: ["Data import"], + summary: "Import follows", operationId: "UserImportController.follow", requestBody: request_body("Parameters", import_request(), required: true), responses: %{ @@ -31,8 +31,8 @@ def follow_operation do def blocks_operation do %Operation{ - tags: ["blocks_import"], - summary: "Imports your blocks.", + tags: ["Data import"], + summary: "Import blocks", operationId: "UserImportController.blocks", requestBody: request_body("Parameters", import_request(), required: true), responses: %{ @@ -45,8 +45,8 @@ def blocks_operation do def mutes_operation do %Operation{ - tags: ["mutes_import"], - summary: "Imports your mutes.", + tags: ["Data import"], + summary: "Import mutes", operationId: "UserImportController.mutes", requestBody: request_body("Parameters", import_request(), required: true), responses: %{ diff --git a/lib/pleroma/web/api_spec/render_error.ex b/lib/pleroma/web/api_spec/render_error.ex index d476b8ef3..e501a6be4 100644 --- a/lib/pleroma/web/api_spec/render_error.ex +++ b/lib/pleroma/web/api_spec/render_error.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.RenderError do diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index 684f6fc92..bd7143ab9 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Account do @@ -40,13 +40,15 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do pleroma: %Schema{ type: :object, properties: %{ + ap_id: %Schema{type: :string}, + also_known_as: %Schema{type: :array, items: %Schema{type: :string}}, allow_following_move: %Schema{ type: :boolean, description: "whether the user allows automatically follow moved following accounts" }, background_image: %Schema{type: :string, nullable: true, format: :uri}, chat_token: %Schema{type: :string}, - confirmation_pending: %Schema{ + is_confirmed: %Schema{ type: :boolean, description: "whether the user account is waiting on email confirmation to be activated" @@ -94,7 +96,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do hide_notification_contents: %Schema{type: :boolean} } }, - relationship: AccountRelationship, + relationship: %Schema{allOf: [AccountRelationship], nullable: true}, settings_store: %Schema{ type: :object, description: @@ -164,7 +166,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do "pleroma" => %{ "allow_following_move" => true, "background_image" => nil, - "confirmation_pending" => true, + "is_confirmed" => false, "hide_favorites" => true, "hide_followers" => false, "hide_followers_count" => false, diff --git a/lib/pleroma/web/api_spec/schemas/account_field.ex b/lib/pleroma/web/api_spec/schemas/account_field.ex index fa97073a0..7c4f94001 100644 --- a/lib/pleroma/web/api_spec/schemas/account_field.ex +++ b/lib/pleroma/web/api_spec/schemas/account_field.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.AccountField do diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship.ex b/lib/pleroma/web/api_spec/schemas/account_relationship.ex index 8b982669e..16b73ebb4 100644 --- a/lib/pleroma/web/api_spec/schemas/account_relationship.ex +++ b/lib/pleroma/web/api_spec/schemas/account_relationship.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do @@ -10,7 +10,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do OpenApiSpex.schema(%{ title: "AccountRelationship", - description: "Response schema for relationship", + description: "Relationship between current account and requested account", type: :object, properties: %{ blocked_by: %Schema{type: :boolean}, diff --git a/lib/pleroma/web/api_spec/schemas/actor_type.ex b/lib/pleroma/web/api_spec/schemas/actor_type.ex index ac9b46678..1336640a1 100644 --- a/lib/pleroma/web/api_spec/schemas/actor_type.ex +++ b/lib/pleroma/web/api_spec/schemas/actor_type.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.ActorType do diff --git a/lib/pleroma/web/api_spec/schemas/api_error.ex b/lib/pleroma/web/api_spec/schemas/api_error.ex index 5815df94c..0d6d0b75c 100644 --- a/lib/pleroma/web/api_spec/schemas/api_error.ex +++ b/lib/pleroma/web/api_spec/schemas/api_error.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.ApiError do diff --git a/lib/pleroma/web/api_spec/schemas/attachment.ex b/lib/pleroma/web/api_spec/schemas/attachment.ex index c6edf6d36..ca3659c93 100644 --- a/lib/pleroma/web/api_spec/schemas/attachment.ex +++ b/lib/pleroma/web/api_spec/schemas/attachment.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Attachment do diff --git a/lib/pleroma/web/api_spec/schemas/boolean_like.ex b/lib/pleroma/web/api_spec/schemas/boolean_like.ex index f3bfb74da..778158f66 100644 --- a/lib/pleroma/web/api_spec/schemas/boolean_like.ex +++ b/lib/pleroma/web/api_spec/schemas/boolean_like.ex @@ -1,8 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.BooleanLike do + alias OpenApiSpex.Cast alias OpenApiSpex.Schema require OpenApiSpex @@ -27,10 +28,13 @@ defmodule Pleroma.Web.ApiSpec.Schemas.BooleanLike do %Schema{type: :boolean}, %Schema{type: :string}, %Schema{type: :integer} - ] + ], + "x-validate": __MODULE__ }) - def after_cast(value, _schmea) do - {:ok, Pleroma.Web.ControllerHelper.truthy_param?(value)} + def cast(%Cast{value: value} = context) do + context + |> Map.put(:value, Pleroma.Web.ControllerHelper.truthy_param?(value)) + |> Cast.ok() end end diff --git a/lib/pleroma/web/api_spec/schemas/chat.ex b/lib/pleroma/web/api_spec/schemas/chat.ex index 65f908e33..4afed910d 100644 --- a/lib/pleroma/web/api_spec/schemas/chat.ex +++ b/lib/pleroma/web/api_spec/schemas/chat.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Chat do @@ -23,7 +23,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Chat do "account" => %{ "pleroma" => %{ "is_admin" => false, - "confirmation_pending" => false, + "is_confirmed" => true, "hide_followers_count" => false, "is_moderator" => false, "hide_favorites" => true, diff --git a/lib/pleroma/web/api_spec/schemas/chat_message.ex b/lib/pleroma/web/api_spec/schemas/chat_message.ex index 9d2799618..348fe95f8 100644 --- a/lib/pleroma/web/api_spec/schemas/chat_message.ex +++ b/lib/pleroma/web/api_spec/schemas/chat_message.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.ChatMessage do @@ -52,7 +52,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.ChatMessage do title: %Schema{type: :string, description: "Title of linked resource"}, description: %Schema{type: :string, description: "Description of preview"} } - } + }, + unread: %Schema{type: :boolean, description: "Whether a message has been marked as read."} }, example: %{ "account_id" => "someflakeid", @@ -69,7 +70,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.ChatMessage do } ], "id" => "14", - "attachment" => nil + "attachment" => nil, + "unread" => false } }) end diff --git a/lib/pleroma/web/api_spec/schemas/conversation.ex b/lib/pleroma/web/api_spec/schemas/conversation.ex index d8ff5ba26..7c609965f 100644 --- a/lib/pleroma/web/api_spec/schemas/conversation.ex +++ b/lib/pleroma/web/api_spec/schemas/conversation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Conversation do diff --git a/lib/pleroma/web/api_spec/schemas/emoji.ex b/lib/pleroma/web/api_spec/schemas/emoji.ex index 26f35e648..ceb3c7186 100644 --- a/lib/pleroma/web/api_spec/schemas/emoji.ex +++ b/lib/pleroma/web/api_spec/schemas/emoji.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Emoji do diff --git a/lib/pleroma/web/api_spec/schemas/flake_id.ex b/lib/pleroma/web/api_spec/schemas/flake_id.ex index 3b5f6477a..45314d53a 100644 --- a/lib/pleroma/web/api_spec/schemas/flake_id.ex +++ b/lib/pleroma/web/api_spec/schemas/flake_id.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.FlakeID do diff --git a/lib/pleroma/web/api_spec/schemas/list.ex b/lib/pleroma/web/api_spec/schemas/list.ex index b7d1685c9..90f5ec987 100644 --- a/lib/pleroma/web/api_spec/schemas/list.ex +++ b/lib/pleroma/web/api_spec/schemas/list.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.List do diff --git a/lib/pleroma/web/api_spec/schemas/poll.ex b/lib/pleroma/web/api_spec/schemas/poll.ex index 0dfa60b97..943ad8bd4 100644 --- a/lib/pleroma/web/api_spec/schemas/poll.ex +++ b/lib/pleroma/web/api_spec/schemas/poll.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Poll do diff --git a/lib/pleroma/web/api_spec/schemas/push_subscription.ex b/lib/pleroma/web/api_spec/schemas/push_subscription.ex index cc91b95b8..20fe9f304 100644 --- a/lib/pleroma/web/api_spec/schemas/push_subscription.ex +++ b/lib/pleroma/web/api_spec/schemas/push_subscription.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.PushSubscription do diff --git a/lib/pleroma/web/api_spec/schemas/scheduled_status.ex b/lib/pleroma/web/api_spec/schemas/scheduled_status.ex index addefa9d3..607586e32 100644 --- a/lib/pleroma/web/api_spec/schemas/scheduled_status.ex +++ b/lib/pleroma/web/api_spec/schemas/scheduled_status.ex @@ -1,12 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.ScheduledStatus do alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Schemas.Attachment - alias Pleroma.Web.ApiSpec.Schemas.Poll alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope + alias Pleroma.Web.ApiSpec.StatusOperation require OpenApiSpex @@ -29,8 +29,9 @@ defmodule Pleroma.Web.ApiSpec.Schemas.ScheduledStatus do spoiler_text: %Schema{type: :string, nullable: true}, visibility: %Schema{allOf: [VisibilityScope], nullable: true}, scheduled_at: %Schema{type: :string, format: :"date-time", nullable: true}, - poll: %Schema{allOf: [Poll], nullable: true}, - in_reply_to_id: %Schema{type: :string, nullable: true} + poll: StatusOperation.poll_params(), + in_reply_to_id: %Schema{type: :string, nullable: true}, + expires_in: %Schema{type: :integer, nullable: true} } } }, @@ -46,7 +47,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.ScheduledStatus do scheduled_at: nil, poll: nil, idempotency: nil, - in_reply_to_id: nil + in_reply_to_id: nil, + expires_in: nil }, media_attachments: [Attachment.schema().example] } diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index e6890df2d..3d042dc19 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Status do @@ -23,9 +23,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do application: %Schema{ description: "The application used to post this status", type: :object, + nullable: true, properties: %{ name: %Schema{type: :string}, - website: %Schema{type: :string, nullable: true, format: :uri} + website: %Schema{type: :string, format: :uri} } }, bookmarked: %Schema{type: :boolean, description: "Have you bookmarked this status?"}, @@ -193,6 +194,13 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do parent_visible: %Schema{ type: :boolean, description: "`true` if the parent post is visible to the user" + }, + pinned_at: %Schema{ + type: :string, + format: "date-time", + nullable: true, + description: + "A datetime (ISO 8601) that states when the post was pinned or `null` if the post is not pinned" } } }, @@ -256,7 +264,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "note" => "Tester Number 6", "pleroma" => %{ "background_image" => nil, - "confirmation_pending" => false, + "is_confirmed" => true, "hide_favorites" => true, "hide_followers" => false, "hide_followers_count" => false, @@ -291,7 +299,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "url" => "http://localhost:4001/users/nick6", "username" => "nick6" }, - "application" => %{"name" => "Web", "website" => nil}, + "application" => nil, "bookmarked" => false, "card" => nil, "content" => "foobar", diff --git a/lib/pleroma/web/api_spec/schemas/tag.ex b/lib/pleroma/web/api_spec/schemas/tag.ex index e693fb83e..657b675e5 100644 --- a/lib/pleroma/web/api_spec/schemas/tag.ex +++ b/lib/pleroma/web/api_spec/schemas/tag.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.Tag do diff --git a/lib/pleroma/web/api_spec/schemas/visibility_scope.ex b/lib/pleroma/web/api_spec/schemas/visibility_scope.ex index 633269a92..25a08a0b2 100644 --- a/lib/pleroma/web/api_spec/schemas/visibility_scope.ex +++ b/lib/pleroma/web/api_spec/schemas/visibility_scope.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.Schemas.VisibilityScope do diff --git a/lib/pleroma/web/auth/authenticator.ex b/lib/pleroma/web/auth/authenticator.ex index b4db312fb..84741ee11 100644 --- a/lib/pleroma/web/auth/authenticator.ex +++ b/lib/pleroma/web/auth/authenticator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.Authenticator do diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index 402ab428b..17e08a2a6 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.LDAPAuthenticator do diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex index d6d2a8d06..401f23c9f 100644 --- a/lib/pleroma/web/auth/pleroma_authenticator.ex +++ b/lib/pleroma/web/auth/pleroma_authenticator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.PleromaAuthenticator do @@ -84,7 +84,7 @@ def create_from_registration( password_confirmation: random_password }, external: true, - need_confirmation: false + confirmed: true ) |> Repo.insert(), {:ok, _} <- diff --git a/lib/pleroma/web/auth/totp_authenticator.ex b/lib/pleroma/web/auth/totp_authenticator.ex index edc9871ea..5947cd8c9 100644 --- a/lib/pleroma/web/auth/totp_authenticator.ex +++ b/lib/pleroma/web/auth/totp_authenticator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.TOTPAuthenticator do diff --git a/lib/pleroma/web/channels/user_socket.ex b/lib/pleroma/web/channels/user_socket.ex index 306ef1916..1c09b6768 100644 --- a/lib/pleroma/web/channels/user_socket.ex +++ b/lib/pleroma/web/channels/user_socket.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.UserSocket do diff --git a/lib/pleroma/web/chat_channel.ex b/lib/pleroma/web/chat_channel.ex index 3b1469c19..4008129e9 100644 --- a/lib/pleroma/web/chat_channel.ex +++ b/lib/pleroma/web/chat_channel.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ChatChannel do diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex index e59254791..1b5f8491e 100644 --- a/lib/pleroma/web/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.CommonAPI do @@ -142,7 +142,7 @@ def delete(activity_id, user) do with {_, %Activity{data: %{"object" => _, "type" => "Create"}} = activity} <- {:find_activity, Activity.get_by_id(activity_id)}, {_, %Object{} = object, _} <- - {:find_object, Object.normalize(activity, false), activity}, + {:find_object, Object.normalize(activity, fetch: false), activity}, true <- User.superuser?(user) || user.ap_id == object.data["actor"], {:ok, delete_data, _} <- Builder.delete(user, object.data["id"]), {:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do @@ -173,7 +173,7 @@ def delete(activity_id, user) do def repeat(id, user, params \\ %{}) do with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id), - object = %Object{} <- Object.normalize(activity, false), + object = %Object{} <- Object.normalize(activity, fetch: false), {_, nil} <- {:existing_announce, Utils.get_existing_announce(user.ap_id, object)}, public = public_announce?(object, params), {:ok, announce, _} <- Builder.announce(user, object, public: public), @@ -191,7 +191,7 @@ def repeat(id, user, params \\ %{}) do def unrepeat(id, user) do with {_, %Activity{data: %{"type" => "Create"}} = activity} <- {:find_activity, Activity.get_by_id(id)}, - %Object{} = note <- Object.normalize(activity, false), + %Object{} = note <- Object.normalize(activity, fetch: false), %Activity{} = announce <- Utils.get_existing_announce(user.ap_id, note), {:ok, undo, _} <- Builder.undo(user, announce), {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do @@ -228,17 +228,7 @@ def favorite_helper(user, id) do {:find_object, _} -> {:error, :not_found} - {:common_pipeline, - { - :error, - { - :validate_object, - { - :error, - changeset - } - } - }} = e -> + {:common_pipeline, {:error, {:validate, {:error, changeset}}}} = e -> if {:object, {"already liked by this actor", []}} in changeset.errors do {:ok, :already_liked} else @@ -253,7 +243,7 @@ def favorite_helper(user, id) do def unfavorite(id, user) do with {_, %Activity{data: %{"type" => "Create"}} = activity} <- {:find_activity, Activity.get_by_id(id)}, - %Object{} = note <- Object.normalize(activity, false), + %Object{} = note <- Object.normalize(activity, fetch: false), %Activity{} = like <- Utils.get_existing_like(user.ap_id, note), {:ok, undo, _} <- Builder.undo(user, like), {:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do @@ -266,7 +256,7 @@ def unfavorite(id, user) do def react_with_emoji(id, user, emoji) do with %Activity{} = activity <- Activity.get_by_id(id), - object <- Object.normalize(activity), + object <- Object.normalize(activity, fetch: false), {:ok, emoji_react, _} <- Builder.emoji_react(user, object, emoji), {:ok, activity, _} <- Pipeline.common_pipeline(emoji_react, local: true) do {:ok, activity} @@ -377,7 +367,7 @@ def get_visibility(_, in_reply_to, _), do: {"public", get_replied_to_visibility( def get_replied_to_visibility(nil), do: nil def get_replied_to_visibility(activity) do - with %Object{} = object <- Object.normalize(activity) do + with %Object{} = object <- Object.normalize(activity, fetch: false) do Visibility.get_visibility(object) end end @@ -411,29 +401,58 @@ def post(user, %{status: _} = data) do end end - def pin(id, %{ap_id: user_ap_id} = user) do - with %Activity{ - actor: ^user_ap_id, - data: %{"type" => "Create"}, - object: %Object{data: %{"type" => object_type}} - } = activity <- Activity.get_by_id_with_object(id), - true <- object_type in ["Note", "Article", "Question"], - true <- Visibility.is_public?(activity), - {:ok, _user} <- User.add_pinnned_activity(user, activity) do + @spec pin(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, term()} + def pin(id, %User{} = user) do + with %Activity{} = activity <- create_activity_by_id(id), + true <- activity_belongs_to_actor(activity, user.ap_id), + true <- object_type_is_allowed_for_pin(activity.object), + true <- activity_is_public(activity), + {:ok, pin_data, _} <- Builder.pin(user, activity.object), + {:ok, _pin, _} <- + Pipeline.common_pipeline(pin_data, + local: true, + activity_id: id + ) do {:ok, activity} else - {:error, %{errors: [pinned_activities: {err, _}]}} -> {:error, err} - _ -> {:error, dgettext("errors", "Could not pin")} + {:error, {:side_effects, error}} -> error + error -> error end end + defp create_activity_by_id(id) do + with nil <- Activity.create_by_id_with_object(id) do + {:error, :not_found} + end + end + + defp activity_belongs_to_actor(%{actor: actor}, actor), do: true + defp activity_belongs_to_actor(_, _), do: {:error, :ownership_error} + + defp object_type_is_allowed_for_pin(%{data: %{"type" => type}}) do + with false <- type in ["Note", "Article", "Question"] do + {:error, :not_allowed} + end + end + + defp activity_is_public(activity) do + with false <- Visibility.is_public?(activity) do + {:error, :visibility_error} + end + end + + @spec unpin(String.t(), User.t()) :: {:ok, User.t()} | {:error, term()} def unpin(id, user) do - with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id), - {:ok, _user} <- User.remove_pinnned_activity(user, activity) do + with %Activity{} = activity <- create_activity_by_id(id), + {:ok, unpin_data, _} <- Builder.unpin(user, activity.object), + {:ok, _unpin, _} <- + Pipeline.common_pipeline(unpin_data, + local: true, + activity_id: activity.id, + expires_at: activity.data["expires_at"], + featured_address: user.featured_address + ) do {:ok, activity} - else - {:error, %{errors: [pinned_activities: {err, _}]}} -> {:error, err} - _ -> {:error, dgettext("errors", "Could not unpin")} end end diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index aa2616d9e..80a9fa7bb 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -1,10 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.CommonAPI.ActivityDraft do alias Pleroma.Activity alias Pleroma.Conversation.Participation + alias Pleroma.Object alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils @@ -179,17 +180,44 @@ defp context(draft) do end defp sensitive(draft) do - sensitive = draft.params[:sensitive] || Enum.member?(draft.tags, {"#nsfw", "nsfw"}) + sensitive = draft.params[:sensitive] %__MODULE__{draft | sensitive: sensitive} end defp object(draft) do emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji) + # Sometimes people create posts with subject containing emoji, + # since subjects are usually copied this will result in a broken + # subject when someone replies from an instance that does not have + # the emoji or has it under different shortcode. This is an attempt + # to mitigate this by copying emoji from inReplyTo if they are present + # in the subject. + summary_emoji = + with %Activity{} <- draft.in_reply_to, + %Object{data: %{"tag" => [_ | _] = tag}} <- Object.normalize(draft.in_reply_to) do + Enum.reduce(tag, %{}, fn + %{"type" => "Emoji", "name" => name, "icon" => %{"url" => url}}, acc -> + if String.contains?(draft.summary, name) do + Map.put(acc, name, url) + else + acc + end + + _, acc -> + acc + end) + else + _ -> %{} + end + + emoji = Map.merge(emoji, summary_emoji) + object = Utils.make_note_data(draft) |> Map.put("emoji", emoji) |> Map.put("source", draft.status) + |> Map.put("generator", draft.params[:generator]) %__MODULE__{draft | object: object} end diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 4731e79be..94a378e11 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.CommonAPI.Utils do @@ -217,7 +217,6 @@ def make_content_html(%ActivityDraft{} = draft) do draft.status |> format_input(content_type, options) |> maybe_add_attachments(draft.attachments, attachment_links) - |> maybe_add_nsfw_tag(draft.params) end defp get_content_type(content_type) do @@ -228,13 +227,6 @@ defp get_content_type(content_type) do end end - defp maybe_add_nsfw_tag({text, mentions, tags}, %{"sensitive" => sensitive}) - when sensitive in [true, "True", "true", "1"] do - {text, mentions, [{"#nsfw", "nsfw"} | tags]} - end - - defp maybe_add_nsfw_tag(data, _), do: data - def make_context(_, %Participation{} = participation) do Repo.preload(participation, :conversation).conversation.ap_id end @@ -319,7 +311,7 @@ def make_note_data(%ActivityDraft{} = draft) do defp add_in_reply_to(object, nil), do: object defp add_in_reply_to(object, in_reply_to) do - with %Object{} = in_reply_to_object <- Object.normalize(in_reply_to) do + with %Object{} = in_reply_to_object <- Object.normalize(in_reply_to, fetch: false) do Map.put(object, "inReplyTo", in_reply_to_object.data["id"]) else _ -> object @@ -399,7 +391,7 @@ def maybe_notify_mentioned_recipients( %Activity{data: %{"to" => _to, "type" => type} = data} = activity ) when type == "Create" do - object = Object.normalize(activity, false) + object = Object.normalize(activity, fetch: false) object_data = cond do diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index 69188a882..61d65e7a3 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ControllerHelper do @@ -67,7 +67,7 @@ def add_link_headers(conn, entries, extra_params) do defp build_pagination_fields(conn, min_id, max_id, extra_params) do params = conn.params - |> Map.drop(Map.keys(conn.path_params)) + |> Map.drop(Map.keys(conn.path_params) |> Enum.map(&String.to_existing_atom/1)) |> Map.merge(extra_params) |> Map.drop(@id_keys) diff --git a/lib/pleroma/web/embed_controller.ex b/lib/pleroma/web/embed_controller.ex index f6b8a5ee1..c7912bb1f 100644 --- a/lib/pleroma/web/embed_controller.ex +++ b/lib/pleroma/web/embed_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.EmbedController do @@ -31,7 +31,7 @@ def show(conn, %{"id" => id}) do end defp get_counts(%Activity{} = activity) do - %Object{data: data} = Object.normalize(activity) + %Object{data: data} = Object.normalize(activity, fetch: false) %{ likes: Map.get(data, "like_count", 0), diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index f26542e88..8e274de88 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Endpoint do @@ -23,6 +23,18 @@ defmodule Pleroma.Web.Endpoint do # InstanceStatic needs to be before Plug.Static to be able to override shipped-static files # If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well # Cache-control headers are duplicated in case we turn off etags in the future + plug( + Pleroma.Web.Plugs.InstanceStatic, + at: "/", + from: :pleroma, + only: ["emoji", "images"], + gzip: true, + cache_control_for_etags: "public, max-age=1209600", + headers: %{ + "cache-control" => "public, max-age=1209600" + } + ) + plug(Pleroma.Web.Plugs.InstanceStatic, at: "/", gzip: true, diff --git a/lib/pleroma/web/fallback/legacy_pleroma_api_rerouter_plug.ex b/lib/pleroma/web/fallback/legacy_pleroma_api_rerouter_plug.ex new file mode 100644 index 000000000..f86d6b52b --- /dev/null +++ b/lib/pleroma/web/fallback/legacy_pleroma_api_rerouter_plug.ex @@ -0,0 +1,26 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Fallback.LegacyPleromaApiRerouterPlug do + alias Pleroma.Web.Endpoint + alias Pleroma.Web.Fallback.RedirectController + + def init(opts), do: opts + + def call(%{path_info: ["api", "pleroma" | path_info_rest]} = conn, _opts) do + new_path_info = ["api", "v1", "pleroma" | path_info_rest] + new_request_path = Enum.join(new_path_info, "/") + + conn + |> Map.merge(%{ + path_info: new_path_info, + request_path: new_request_path + }) + |> Endpoint.call(conn.params) + end + + def call(conn, _opts) do + RedirectController.api_not_implemented(conn, %{}) + end +end diff --git a/lib/pleroma/web/fallback/redirect_controller.ex b/lib/pleroma/web/fallback/redirect_controller.ex index 1ac1319f8..5fca290e5 100644 --- a/lib/pleroma/web/fallback/redirect_controller.ex +++ b/lib/pleroma/web/fallback/redirect_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Fallback.RedirectController do diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex index 130654145..f5ef76d32 100644 --- a/lib/pleroma/web/federator.ex +++ b/lib/pleroma/web/federator.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Federator do @@ -15,6 +15,8 @@ defmodule Pleroma.Web.Federator do require Logger + @behaviour Pleroma.Web.Federator.Publishing + @doc """ Returns `true` if the distance to target object does not exceed max configured value. Serves to prevent fetching of very long threads, especially useful on smaller instances. @@ -39,10 +41,12 @@ def incoming_ap_doc(params) do ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params}) end + @impl true def publish(%{id: "pleroma:fakeid"} = activity) do perform(:publish, activity) end + @impl true def publish(activity) do PublisherWorker.enqueue("publish", %{"activity_id" => activity.id}) end diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index ad0201361..b7ee56803 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Federator.Publisher do diff --git a/lib/pleroma/web/federator/publishing.ex b/lib/pleroma/web/federator/publishing.ex new file mode 100644 index 000000000..fe7805be9 --- /dev/null +++ b/lib/pleroma/web/federator/publishing.ex @@ -0,0 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Federator.Publishing do + @callback publish(map()) :: any() +end diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex index 56c024617..66940f311 100644 --- a/lib/pleroma/web/feed/feed_view.ex +++ b/lib/pleroma/web/feed/feed_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Feed.FeedView do @@ -23,7 +23,7 @@ def pub_date(date) when is_binary(date) do def pub_date(%DateTime{} = date), do: Timex.format!(date, "{RFC822}") def prepare_activity(activity, opts \\ []) do - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) actor = if opts[:actor] do @@ -32,6 +32,7 @@ def prepare_activity(activity, opts \\ []) do %{ activity: activity, + object: object, data: Map.get(object, :data), actor: actor } @@ -51,7 +52,7 @@ def most_recent_update(activities, user) do def feed_logo do case Pleroma.Config.get([:feed, :logo]) do nil -> - "#{Pleroma.Web.base_url()}/static/logo.png" + "#{Pleroma.Web.base_url()}/static/logo.svg" logo -> "#{Pleroma.Web.base_url()}#{logo}" diff --git a/lib/pleroma/web/feed/tag_controller.ex b/lib/pleroma/web/feed/tag_controller.ex index 218cdbdf3..ef9293a55 100644 --- a/lib/pleroma/web/feed/tag_controller.ex +++ b/lib/pleroma/web/feed/tag_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Feed.TagController do diff --git a/lib/pleroma/web/feed/user_controller.ex b/lib/pleroma/web/feed/user_controller.ex index a5013d2c0..58d35da1e 100644 --- a/lib/pleroma/web/feed/user_controller.ex +++ b/lib/pleroma/web/feed/user_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Feed.UserController do diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index 0adf428ec..c0ca4d0e9 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Gettext do diff --git a/lib/pleroma/web/instance_document.ex b/lib/pleroma/web/instance_document.ex index df5caebf0..a33bf605b 100644 --- a/lib/pleroma/web/instance_document.ex +++ b/lib/pleroma/web/instance_document.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.InstanceDocument do diff --git a/lib/pleroma/web/mailer/subscription_controller.ex b/lib/pleroma/web/mailer/subscription_controller.ex index ace44afd1..f89abe46a 100644 --- a/lib/pleroma/web/mailer/subscription_controller.ex +++ b/lib/pleroma/web/mailer/subscription_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Mailer.SubscriptionController do diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex index 20279ff45..e788ab37a 100644 --- a/lib/pleroma/web/masto_fe_controller.ex +++ b/lib/pleroma/web/masto_fe_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastoFEController do diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index b4375872b..7a1e99044 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AccountController do @@ -184,6 +184,7 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p :show_role, :skip_thread_containment, :allow_following_move, + :also_known_as, :accepts_chat_messages ] |> Enum.reduce(%{}, fn key, acc -> @@ -207,6 +208,7 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p if bot, do: {:ok, "Service"}, else: {:ok, "Person"} end) |> Maps.put_if_present(:actor_type, params[:actor_type]) + |> Maps.put_if_present(:also_known_as, params[:also_known_as]) # Note: param name is indeed :locked (not an error) |> Maps.put_if_present(:is_locked, params[:locked]) # Note: param name is indeed :discoverable (not an error) @@ -267,10 +269,14 @@ def relationships(%{assigns: %{user: user}} = conn, %{id: id}) do def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, []) @doc "GET /api/v1/accounts/:id" - def show(%{assigns: %{user: for_user}} = conn, %{id: nickname_or_id}) do + def show(%{assigns: %{user: for_user}} = conn, %{id: nickname_or_id} = params) do with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user), :visible <- User.visible_for(user, for_user) do - render(conn, "show.json", user: user, for: for_user) + render(conn, "show.json", + user: user, + for: for_user, + embed_relationships: embed_relationships?(params) + ) else error -> user_visibility_error(conn, error) end @@ -452,7 +458,12 @@ def mutes(%{assigns: %{user: user}} = conn, params) do conn |> add_link_headers(users) - |> render("index.json", users: users, for: user, as: :user) + |> render("index.json", + users: users, + for: user, + as: :user, + embed_relationships: embed_relationships?(params) + ) end @doc "GET /api/v1/blocks" diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index 143dcf80c..dd3b39c77 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -1,8 +1,13 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AppController do + @moduledoc """ + Controller for supporting app-related actions. + If authentication is an option, app tokens (user-unbound) must be supported. + """ + use Pleroma.Web, :controller alias Pleroma.Repo @@ -17,11 +22,9 @@ defmodule Pleroma.Web.MastodonAPI.AppController do plug( :skip_plug, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] - when action == :create + when action in [:create, :verify_credentials] ) - plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials) - plug(Pleroma.Web.ApiSpec.CastAndValidate) @local_mastodon_name "Mastodon-Local" @@ -44,10 +47,13 @@ def create(%{body_params: params} = conn, _params) do end end - @doc "GET /api/v1/apps/verify_credentials" - def verify_credentials(%{assigns: %{user: _user, token: token}} = conn, _) do - with %Token{app: %App{} = app} <- Repo.preload(token, :app) do - render(conn, "short.json", app: app) + @doc """ + GET /api/v1/apps/verify_credentials + Gets compact non-secret representation of the app. Supports app tokens and user tokens. + """ + def verify_credentials(%{assigns: %{token: %Token{} = token}} = conn, _) do + with %{app: %App{} = app} <- Repo.preload(token, :app) do + render(conn, "compact_non_secret.json", app: app) end end end diff --git a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex index 93d057a79..eb6639fc5 100644 --- a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AuthController do diff --git a/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex b/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex index 61347d8db..f2a0949e8 100644 --- a/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ConversationController do @@ -36,4 +36,13 @@ def mark_as_read(%{assigns: %{user: user}} = conn, %{id: participation_id}) do render(conn, "participation.json", participation: participation, for: user) end end + + @doc "DELETE /api/v1/conversations/:id" + def delete(%{assigns: %{user: user}} = conn, %{id: participation_id}) do + with %Participation{} = participation <- + Repo.get_by(Participation, id: participation_id, user_id: user.id), + {:ok, _} <- Participation.delete(participation) do + json(conn, %{}) + end + end end diff --git a/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex b/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex index 872cb1f4d..d7e18dc92 100644 --- a/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.CustomEmojiController do 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 503bd7d5f..30300307d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.DomainBlockController do diff --git a/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex b/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex index 8af557b61..84621500e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.FallbackController do @@ -30,6 +30,12 @@ def call(conn, {:error, error_message}) do |> json(%{error: error_message}) end + def call(conn, {:error, status, message}) do + conn + |> put_status(status) + |> json(%{error: message}) + end + def call(conn, _) do conn |> put_status(:internal_server_error) diff --git a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex index c71a34b15..9b1ae809d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.FilterController do @@ -20,6 +20,8 @@ defmodule Pleroma.Web.MastodonAPI.FilterController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.FilterOperation + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + @doc "GET /api/v1/filters" def index(%{assigns: %{user: user}} = conn, _) do filters = Filter.get_filters(user) @@ -29,25 +31,23 @@ def index(%{assigns: %{user: user}} = conn, _) do @doc "POST /api/v1/filters" def create(%{assigns: %{user: user}, body_params: params} = conn, _) do - query = %Filter{ - user_id: user.id, - phrase: params.phrase, - context: params.context, - hide: params.irreversible, - whole_word: params.whole_word - # TODO: support `expires_in` parameter (as in Mastodon API) - } - - {:ok, response} = Filter.create(query) - - render(conn, "show.json", filter: response) + with {:ok, response} <- + params + |> Map.put(:user_id, user.id) + |> Map.put(:hide, params[:irreversible]) + |> Map.delete(:irreversible) + |> Filter.create() do + render(conn, "show.json", filter: response) + end end @doc "GET /api/v1/filters/:id" def show(%{assigns: %{user: user}} = conn, %{id: filter_id}) do - filter = Filter.get(filter_id, user) - - render(conn, "show.json", filter: filter) + with %Filter{} = filter <- Filter.get(filter_id, user) do + render(conn, "show.json", filter: filter) + else + nil -> {:error, :not_found} + end end @doc "PUT /api/v1/filters/:id" @@ -56,28 +56,31 @@ def update( %{id: filter_id} ) do params = - params - |> Map.delete(:irreversible) - |> Map.put(:hide, params[:irreversible]) - |> Enum.reject(fn {_key, value} -> is_nil(value) end) - |> Map.new() - - # TODO: support `expires_in` parameter (as in Mastodon API) + if is_boolean(params[:irreversible]) do + params + |> Map.put(:hide, params[:irreversible]) + |> Map.delete(:irreversible) + else + params + end with %Filter{} = filter <- Filter.get(filter_id, user), {:ok, %Filter{} = filter} <- Filter.update(filter, params) do render(conn, "show.json", filter: filter) + else + nil -> {:error, :not_found} + error -> error end end @doc "DELETE /api/v1/filters/:id" def delete(%{assigns: %{user: user}} = conn, %{id: filter_id}) do - query = %Filter{ - user_id: user.id, - filter_id: filter_id - } - - {:ok, _} = Filter.delete(query) - json(conn, %{}) + with %Filter{} = filter <- Filter.get(filter_id, user), + {:ok, _} <- Filter.delete(filter) do + json(conn, %{}) + else + nil -> {:error, :not_found} + error -> error + end end end 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 f8cd7fa9f..63d0e2c35 100644 --- a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.FollowRequestController do diff --git a/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex b/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex index 07a32491a..c7a5267d4 100644 --- a/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex @@ -1,11 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.InstanceController do use Pleroma.Web, :controller - plug(OpenApiSpex.Plug.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate) plug( :skip_plug, diff --git a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex index f6b51bf02..b7b41f449 100644 --- a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ListController do diff --git a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex index 0628b2b49..c745f3493 100644 --- a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MarkerController do 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 9cf682c7b..a1bcc91d9 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex index 161193134..d6949ed80 100644 --- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MediaController do diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index c3c8606f2..647ba661e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.NotificationController do diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index 3dcd1c44f..f44ff997d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.PollController do @@ -26,6 +26,8 @@ defmodule Pleroma.Web.MastodonAPI.PollController do defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PollOperation + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + @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), @@ -55,7 +57,7 @@ def vote(%{assigns: %{user: user}, body_params: %{choices: choices}} = conn, %{i defp get_cached_vote_or_vote(user, object, choices) do idempotency_key = "polls:#{user.id}:#{object.data["id"]}" - Cachex.fetch!(:idempotency_cache, idempotency_key, fn -> + @cachex.fetch!(:idempotency_cache, idempotency_key, fn _ -> case CommonAPI.vote(user, object, choices) do {:error, _message} = res -> {:ignore, res} res -> {:commit, res} diff --git a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex index 156544f40..03d9a4f4f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ReportController 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 322a46497..3b7a0c788 100644 --- a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index 0043c3a56..af93e453d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SearchController do diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 9e3a584f0..724dc5c5d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.StatusController do @@ -21,6 +21,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.ScheduledActivityView + alias Pleroma.Web.OAuth.Token alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.RateLimiter @@ -138,7 +139,9 @@ def create( _ ) when not is_nil(scheduled_at) do - params = Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id]) + params = + Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id]) + |> put_application(conn) attrs = %{ params: Map.new(params, fn {key, value} -> {to_string(key), value} end), @@ -162,7 +165,9 @@ def create( # Creates a regular status def create(%{assigns: %{user: user}, body_params: %{status: _} = params} = conn, _) do - params = Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id]) + params = + Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id]) + |> put_application(conn) with {:ok, activity} <- CommonAPI.post(user, params) do try_render(conn, "show.json", @@ -255,6 +260,18 @@ def unfavourite(%{assigns: %{user: user}} = conn, %{id: activity_id}) do def pin(%{assigns: %{user: user}} = conn, %{id: ap_id_or_id}) do with {:ok, activity} <- CommonAPI.pin(ap_id_or_id, user) do try_render(conn, "show.json", activity: activity, for: user, as: :activity) + else + {:error, :pinned_statuses_limit_reached} -> + {:error, "You have already pinned the maximum number of statuses"} + + {:error, :ownership_error} -> + {:error, :unprocessable_entity, "Someone else's status cannot be pinned"} + + {:error, :visibility_error} -> + {:error, :unprocessable_entity, "Non-public status cannot be pinned"} + + error -> + error end end @@ -318,7 +335,7 @@ def favourited_by(%{assigns: %{user: user}} = conn, %{id: id}) do with true <- Pleroma.Config.get([:instance, :show_reactions]), %Activity{} = activity <- Activity.get_by_id_with_object(id), {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)}, - %Object{data: %{"likes" => likes}} <- Object.normalize(activity) do + %Object{data: %{"likes" => likes}} <- Object.normalize(activity, fetch: false) do users = User |> Ecto.Query.where([u], u.ap_id in ^likes) @@ -339,7 +356,7 @@ def reblogged_by(%{assigns: %{user: user}} = conn, %{id: id}) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)}, %Object{data: %{"announcements" => announces, "id" => ap_id}} <- - Object.normalize(activity) do + Object.normalize(activity, fetch: false) do announces = "Announce" |> Activity.Queries.by_type() @@ -414,4 +431,15 @@ def bookmarks(%{assigns: %{user: user}} = conn, params) do as: :activity ) end + + defp put_application(params, %{assigns: %{token: %Token{user: %User{} = user} = token}} = _conn) do + if user.disclose_client do + %{client_name: client_name, website: website} = Repo.preload(token, :app).app + Map.put(params, :generator, %{type: "Application", name: client_name, url: website}) + else + Map.put(params, :generator, nil) + end + end + + defp put_application(params, _), do: Map.put(params, :generator, nil) end diff --git a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex index 20138908c..fcb3d4829 100644 --- a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SubscriptionController do diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index 5765271cf..01e122dd9 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SuggestionController do diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 852bd0695..c611958be 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.TimelineController do @@ -51,6 +51,8 @@ def home(%{assigns: %{user: user}} = conn, params) do |> Map.put(:reply_filtering_user, user) |> Map.put(:announce_filtering_user, user) |> Map.put(:user, user) + |> Map.put(:local_only, params[:local]) + |> Map.delete(:local) activities = [user.ap_id | User.following(user)] @@ -131,34 +133,25 @@ defp fail_on_bad_auth(conn) do end defp hashtag_fetching(params, user, local_only) do - tags = + # Note: not sanitizing tag options at this stage (may be mix-cased, have duplicates etc.) + tags_any = [params[:tag], params[:any]] |> List.flatten() - |> Enum.uniq() - |> Enum.reject(&is_nil/1) - |> Enum.map(&String.downcase/1) + |> Enum.filter(& &1) - tag_all = - params - |> Map.get(:all, []) - |> Enum.map(&String.downcase/1) + tag_all = Map.get(params, :all, []) + tag_reject = Map.get(params, :none, []) - tag_reject = - params - |> Map.get(:none, []) - |> Enum.map(&String.downcase/1) - - _activities = - params - |> Map.put(:type, "Create") - |> Map.put(:local_only, local_only) - |> Map.put(:blocking_user, user) - |> Map.put(:muting_user, user) - |> Map.put(:user, user) - |> Map.put(:tag, tags) - |> Map.put(:tag_all, tag_all) - |> Map.put(:tag_reject, tag_reject) - |> ActivityPub.fetch_public_activities() + params + |> Map.put(:type, "Create") + |> Map.put(:local_only, local_only) + |> Map.put(:blocking_user, user) + |> Map.put(:muting_user, user) + |> Map.put(:user, user) + |> Map.put(:tag, tags_any) + |> Map.put(:tag_all, tag_all) + |> Map.put(:tag_reject, tag_reject) + |> ActivityPub.fetch_public_activities() end # GET /api/v1/timelines/tag/:tag @@ -190,6 +183,7 @@ def list(%{assigns: %{user: user}} = conn, %{list_id: id} = params) do |> Map.put(:blocking_user, user) |> Map.put(:user, user) |> Map.put(:muting_user, user) + |> Map.put(:local_only, params[:local]) # we must filter the following list for the user to avoid leaking statuses the user # does not actually have permission to see (for more info, peruse security issue #270). diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index 694bf5ca8..71479550e 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MastodonAPI do diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 3158d09ed..ac25aefdd 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AccountView do @@ -187,18 +187,14 @@ defp do_render("show.json", %{user: user} = opts) do header_static = User.banner_url(user) |> MediaProxy.preview_url(static: true) following_count = - if !user.hide_follows_count or !user.hide_follows or opts[:for] == user do - user.following_count || 0 - else - 0 - end + if !user.hide_follows_count or !user.hide_follows or opts[:for] == user, + do: user.following_count, + else: 0 followers_count = - if !user.hide_followers_count or !user.hide_followers or opts[:for] == user do - user.follower_count || 0 - else - 0 - end + if !user.hide_followers_count or !user.hide_followers or opts[:for] == user, + do: user.follower_count, + else: 0 bot = user.actor_type == "Service" @@ -266,10 +262,13 @@ defp do_render("show.json", %{user: user} = opts) do } }, - # Pleroma extension + # Pleroma extensions + # Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub + fqn: User.full_nickname(user), pleroma: %{ ap_id: user.ap_id, - confirmation_pending: user.confirmation_pending, + also_known_as: user.also_known_as, + is_confirmed: user.is_confirmed, tags: user.tags, hide_followers_count: user.hide_followers_count, hide_follows_count: user.hide_follows_count, @@ -379,7 +378,7 @@ defp maybe_put_allow_following_move(data, %User{id: user_id} = user, %User{id: u defp maybe_put_allow_following_move(data, _, _), do: data defp maybe_put_activation_status(data, user, %User{is_admin: true}) do - Kernel.put_in(data, [:pleroma, :deactivated], user.deactivated) + Kernel.put_in(data, [:pleroma, :deactivated], !user.is_active) end defp maybe_put_activation_status(data, _, _), do: data diff --git a/lib/pleroma/web/mastodon_api/views/app_view.ex b/lib/pleroma/web/mastodon_api/views/app_view.ex index e44272c6f..c406b5a27 100644 --- a/lib/pleroma/web/mastodon_api/views/app_view.ex +++ b/lib/pleroma/web/mastodon_api/views/app_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AppView do @@ -34,10 +34,10 @@ def render("show.json", %{app: %App{} = app}) do |> with_vapid_key() end - def render("short.json", %{app: %App{website: webiste, client_name: name}}) do + def render("compact_non_secret.json", %{app: %App{website: website, client_name: name}}) do %{ name: name, - website: webiste + website: website } |> with_vapid_key() end diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex index 82fcff062..46b63b54b 100644 --- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex +++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ConversationView do diff --git a/lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex b/lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex index 47a242b8e..40e314164 100644 --- a/lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex +++ b/lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.CustomEmojiView do diff --git a/lib/pleroma/web/mastodon_api/views/filter_view.ex b/lib/pleroma/web/mastodon_api/views/filter_view.ex index c37f624e0..8e8798c1e 100644 --- a/lib/pleroma/web/mastodon_api/views/filter_view.ex +++ b/lib/pleroma/web/mastodon_api/views/filter_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.FilterView do diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index c5aca5506..dac68d8e6 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.InstanceView do @@ -23,7 +23,8 @@ def render("show.json", _) do streaming_api: Pleroma.Web.Endpoint.websocket_url() }, stats: Pleroma.Stats.get_stats(), - thumbnail: Pleroma.Web.base_url() <> Keyword.get(instance, :instance_thumbnail), + thumbnail: + URI.merge(Pleroma.Web.base_url(), Keyword.get(instance, :instance_thumbnail)) |> to_string, languages: ["en"], registrations: Keyword.get(instance, :registrations_open), approval_required: Keyword.get(instance, :account_approval_required), @@ -45,6 +46,7 @@ def render("show.json", _) do fields_limits: fields_limits(), post_formats: Config.get([:instance, :allowed_post_formats]) }, + stats: %{mau: Pleroma.User.active_user_count()}, vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) } } diff --git a/lib/pleroma/web/mastodon_api/views/list_view.ex b/lib/pleroma/web/mastodon_api/views/list_view.ex index 580596b64..931e77769 100644 --- a/lib/pleroma/web/mastodon_api/views/list_view.ex +++ b/lib/pleroma/web/mastodon_api/views/list_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ListView do diff --git a/lib/pleroma/web/mastodon_api/views/marker_view.ex b/lib/pleroma/web/mastodon_api/views/marker_view.ex index 21d535d54..0c1880935 100644 --- a/lib/pleroma/web/mastodon_api/views/marker_view.ex +++ b/lib/pleroma/web/mastodon_api/views/marker_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MarkerView do diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index 5b06a6b51..df9bedfed 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.NotificationView do @@ -139,7 +139,7 @@ defp put_emoji(response, activity) do end defp put_chat_message(response, activity, reading_user, opts) do - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) author = User.get_cached_by_ap_id(object.data["actor"]) chat = Pleroma.Chat.get(reading_user.id, author.ap_id) cm_ref = MessageReference.for_chat_and_object(chat, object) diff --git a/lib/pleroma/web/mastodon_api/views/poll_view.ex b/lib/pleroma/web/mastodon_api/views/poll_view.ex index 4101f21d0..71bc8b949 100644 --- a/lib/pleroma/web/mastodon_api/views/poll_view.ex +++ b/lib/pleroma/web/mastodon_api/views/poll_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.PollView do @@ -11,7 +11,7 @@ def render("show.json", %{object: object, multiple: multiple, options: options} {end_time, expired} = end_time_and_expired(object) {options, votes_count} = options_and_votes_count(options) - %{ + poll = %{ # Mastodon uses separate ids for polls, but an object can't have # more than one poll embedded so object id is fine id: to_string(object.id), @@ -21,9 +21,16 @@ def render("show.json", %{object: object, multiple: multiple, options: options} votes_count: votes_count, voters_count: voters_count(object), options: options, - voted: voted?(params), emojis: Pleroma.Web.MastodonAPI.StatusView.build_emojis(object.data["emoji"]) } + + if params[:for] do + # when unauthenticated Mastodon doesn't include `voted` & `own_votes` keys in response + {voted, own_votes} = voted_and_own_votes(params, options) + Map.merge(poll, %{voted: voted, own_votes: own_votes}) + else + poll + end end def render("show.json", %{object: object} = params) do @@ -67,12 +74,29 @@ defp voters_count(%{data: %{"voters" => [_ | _] = voters}}) do defp voters_count(_), do: 0 - defp voted?(%{object: object} = opts) do - if opts[:for] do - existing_votes = Pleroma.Web.ActivityPub.Utils.get_existing_votes(opts[:for].ap_id, object) - existing_votes != [] or opts[:for].ap_id == object.data["actor"] + defp voted_and_own_votes(%{object: object} = params, options) do + if params[:for] do + existing_votes = + Pleroma.Web.ActivityPub.Utils.get_existing_votes(params[:for].ap_id, object) + + voted = existing_votes != [] or params[:for].ap_id == object.data["actor"] + + own_votes = + if voted do + titles = Enum.map(options, & &1[:title]) + + Enum.reduce(existing_votes, [], fn vote, acc -> + data = vote |> Map.get(:object) |> Map.get(:data) + index = Enum.find_index(titles, &(&1 == data["name"])) + [index | acc] + end) + else + [] + end + + {voted, own_votes} else - false + {false, []} end end end diff --git a/lib/pleroma/web/mastodon_api/views/report_view.ex b/lib/pleroma/web/mastodon_api/views/report_view.ex index 98cb581ef..0ff347ade 100644 --- a/lib/pleroma/web/mastodon_api/views/report_view.ex +++ b/lib/pleroma/web/mastodon_api/views/report_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ReportView do diff --git a/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex b/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex index 5b896bf3b..453221f41 100644 --- a/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex +++ b/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do @@ -37,7 +37,8 @@ defp status_params(params) do visibility: params["visibility"], scheduled_at: params["scheduled_at"], poll: params["poll"], - in_reply_to_id: params["in_reply_to_id"] + in_reply_to_id: params["in_reply_to_id"], + expires_in: params["expires_in"] } |> Pleroma.Maps.put_if_present(:media_ids, params["media_ids"]) end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 2301e21cf..814b3d142 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.StatusView do @@ -41,7 +41,7 @@ defp get_replied_to_activities(activities) do activities |> Enum.map(fn %{data: %{"type" => "Create"}} = activity -> - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) object && object.data["inReplyTo"] != "" && object.data["inReplyTo"] _ -> @@ -51,7 +51,7 @@ defp get_replied_to_activities(activities) do |> Activity.create_by_object_ap_id_with_object() |> Repo.all() |> Enum.reduce(%{}, fn activity, acc -> - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) if object, do: Map.put(acc, object.data["id"], activity), else: acc end) end @@ -65,7 +65,7 @@ defp get_context_id(%{data: %{"context" => context}}) when is_binary(context), defp get_context_id(_), do: nil defp reblogged?(activity, user) do - object = Object.normalize(activity) || %{} + object = Object.normalize(activity, fetch: false) || %{} present?(user && user.ap_id in (object.data["announcements"] || [])) end @@ -84,7 +84,7 @@ def render("index.json", opts) do parent_activities = activities |> Enum.filter(&(&1.data["type"] == "Announce" && &1.data["object"])) - |> Enum.map(&Object.normalize(&1).data["id"]) + |> Enum.map(&Object.normalize(&1, fetch: false).data["id"]) |> Activity.create_by_object_ap_id() |> Activity.with_preloaded_object(:left) |> Activity.with_preloaded_bookmark(reading_user) @@ -124,16 +124,16 @@ def render( ) do user = CommonAPI.get_user(activity.data["actor"]) created_at = Utils.to_masto_date(activity.data["published"]) - activity_object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) reblogged_parent_activity = if opts[:parent_activities] do Activity.Queries.find_by_object_ap_id( opts[:parent_activities], - activity_object.data["id"] + object.data["id"] ) else - Activity.create_by_object_ap_id(activity_object.data["id"]) + Activity.create_by_object_ap_id(object.data["id"]) |> Activity.with_preloaded_bookmark(opts[:for]) |> Activity.with_set_thread_muted_field(opts[:for]) |> Repo.one() @@ -142,7 +142,7 @@ def render( reblog_rendering_opts = Map.put(opts, :activity, reblogged_parent_activity) reblogged = render("show.json", reblog_rendering_opts) - favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || []) + favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || []) bookmarked = Activity.get_bookmark(reblogged_parent_activity, opts[:for]) != nil @@ -152,10 +152,12 @@ def render( |> Enum.filter(& &1) |> Enum.map(fn user -> AccountView.render("mention.json", %{user: user}) end) + {pinned?, pinned_at} = pin_data(object, user) + %{ id: to_string(activity.id), - uri: activity_object.data["id"], - url: activity_object.data["id"], + uri: object.data["id"], + url: object.data["id"], account: AccountView.render("show.json", %{ user: user, @@ -173,27 +175,25 @@ def render( favourited: present?(favorited), bookmarked: present?(bookmarked), muted: false, - pinned: pinned?(activity, user), + pinned: pinned?, sensitive: false, spoiler_text: "", visibility: get_visibility(activity), media_attachments: reblogged[:media_attachments] || [], mentions: mentions, tags: reblogged[:tags] || [], - application: %{ - name: "Web", - website: nil - }, + application: build_application(object.data["generator"]), language: nil, emojis: [], pleroma: %{ - local: activity.local + local: activity.local, + pinned_at: pinned_at } } end def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) user = CommonAPI.get_user(activity.data["actor"]) user_follower_address = user.follower_address @@ -201,8 +201,10 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} like_count = object.data["like_count"] || 0 announcement_count = object.data["announcement_count"] || 0 - tags = object.data["tag"] || [] - sensitive = object.data["sensitive"] || Enum.member?(tags, "nsfw") + hashtags = Object.hashtags(object) + sensitive = object.data["sensitive"] || Enum.member?(hashtags, "nsfw") + + tags = Object.tags(object) tag_mentions = tags @@ -317,6 +319,8 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} fn for_user, user -> User.mutes?(for_user, user) end ) + {pinned?, pinned_at} = pin_data(object, user) + %{ id: to_string(activity.id), uri: object.data["id"], @@ -340,7 +344,7 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} favourited: present?(favorited), bookmarked: present?(bookmarked), muted: muted, - pinned: pinned?(activity, user), + pinned: pinned?, sensitive: sensitive, spoiler_text: summary, visibility: get_visibility(object), @@ -348,10 +352,7 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} poll: render(PollView, "show.json", object: object, for: opts[:for]), mentions: mentions, tags: build_tags(tags), - application: %{ - name: "Web", - website: nil - }, + application: build_application(object.data["generator"]), language: nil, emojis: build_emojis(object.data["emoji"]), pleroma: %{ @@ -364,7 +365,8 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} direct_conversation_id: direct_conversation_id, thread_muted: thread_muted?, emoji_reactions: emoji_reactions, - parent_visible: visible_for_user?(reply_to, opts[:for]) + parent_visible: visible_for_user?(reply_to, opts[:for]), + pinned_at: pinned_at } } end @@ -385,12 +387,15 @@ def render("card.json", %{rich_media: rich_media, page_url: page_url}) do page_url = page_url_data |> to_string - image_url = + image_url_data = if is_binary(rich_media["image"]) do - URI.merge(page_url_data, URI.parse(rich_media["image"])) - |> to_string + URI.parse(rich_media["image"]) + else + nil end + image_url = build_image_url(image_url_data, page_url_data) + %{ type: "link", provider_name: page_url_data.host, @@ -451,7 +456,7 @@ def render("context.json", %{activity: activity, activities: activities, user: u end def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) with nil <- replied_to_activities[object.data["inReplyTo"]] do # If user didn't participate in the thread @@ -460,7 +465,7 @@ def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do end def get_reply_to(%{data: %{"object" => _object}} = activity, _) do - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) if object.data["inReplyTo"] && object.data["inReplyTo"] != "" do Activity.get_create_by_object_ap_id(object.data["inReplyTo"]) @@ -491,7 +496,7 @@ def render_content(object), do: object.data["content"] || "" def build_tags(object_tags) when is_list(object_tags) do object_tags |> Enum.filter(&is_binary/1) - |> Enum.map(&%{name: &1, url: "/tag/#{URI.encode(&1)}"}) + |> Enum.map(&%{name: &1, url: "#{Pleroma.Web.base_url()}/tag/#{URI.encode(&1)}"}) end def build_tags(_), do: [] @@ -530,8 +535,13 @@ defp present?(nil), do: false defp present?(false), do: false defp present?(_), do: true - defp pinned?(%Activity{id: id}, %User{pinned_activities: pinned_activities}), - do: id in pinned_activities + defp pin_data(%Object{data: %{"id" => object_id}}, %User{pinned_objects: pinned_objects}) do + if pinned_at = pinned_objects[object_id] do + {true, Utils.to_masto_date(pinned_at)} + else + {false, nil} + end + end defp build_emoji_map(emoji, users, current_user) do %{ @@ -540,4 +550,29 @@ defp build_emoji_map(emoji, users, current_user) do me: !!(current_user && current_user.ap_id in users) } end + + @spec build_application(map() | nil) :: map() | nil + defp build_application(%{"type" => _type, "name" => name, "url" => url}), + do: %{name: name, website: url} + + defp build_application(_), do: nil + + # Workaround for Elixir issue #10771 + # Avoid applying URI.merge unless necessary + # TODO: revert to always attempting URI.merge(image_url_data, page_url_data) + # when Elixir 1.12 is the minimum supported version + @spec build_image_url(struct() | nil, struct()) :: String.t() | nil + defp build_image_url( + %URI{scheme: image_scheme, host: image_host} = image_url_data, + %URI{} = _page_url_data + ) + when not is_nil(image_scheme) and not is_nil(image_host) do + image_url_data |> to_string + end + + defp build_image_url(%URI{} = image_url_data, %URI{} = page_url_data) do + URI.merge(page_url_data, image_url_data) |> to_string + end + + defp build_image_url(_, _), do: nil end diff --git a/lib/pleroma/web/mastodon_api/views/subscription_view.ex b/lib/pleroma/web/mastodon_api/views/subscription_view.ex index 7c67cc924..a07d23512 100644 --- a/lib/pleroma/web/mastodon_api/views/subscription_view.ex +++ b/lib/pleroma/web/mastodon_api/views/subscription_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SubscriptionView do diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 439cdd716..0d1faffbd 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do diff --git a/lib/pleroma/web/media_proxy.ex b/lib/pleroma/web/media_proxy.ex index 8656b8cad..d0d4bb4b3 100644 --- a/lib/pleroma/web/media_proxy.ex +++ b/lib/pleroma/web/media_proxy.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy do @@ -12,29 +12,31 @@ defmodule Pleroma.Web.MediaProxy do @base64_opts [padding: false] @cache_table :banned_urls_cache + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + def cache_table, do: @cache_table @spec in_banned_urls(String.t()) :: boolean() - def in_banned_urls(url), do: elem(Cachex.exists?(@cache_table, url(url)), 1) + def in_banned_urls(url), do: elem(@cachex.exists?(@cache_table, url(url)), 1) def remove_from_banned_urls(urls) when is_list(urls) do - Cachex.execute!(@cache_table, fn cache -> - Enum.each(Invalidation.prepare_urls(urls), &Cachex.del(cache, &1)) + @cachex.execute!(@cache_table, fn cache -> + Enum.each(Invalidation.prepare_urls(urls), &@cachex.del(cache, &1)) end) end def remove_from_banned_urls(url) when is_binary(url) do - Cachex.del(@cache_table, url(url)) + @cachex.del(@cache_table, url(url)) end def put_in_banned_urls(urls) when is_list(urls) do - Cachex.execute!(@cache_table, fn cache -> - Enum.each(Invalidation.prepare_urls(urls), &Cachex.put(cache, &1, true)) + @cachex.execute!(@cache_table, fn cache -> + Enum.each(Invalidation.prepare_urls(urls), &@cachex.put(cache, &1, true)) end) end def put_in_banned_urls(url) when is_binary(url) do - Cachex.put(@cache_table, url(url), true) + @cachex.put(@cache_table, url(url), true) end def url(url) when is_nil(url) or url == "", do: nil @@ -67,7 +69,7 @@ def enabled?, do: Config.get([:media_proxy, :enabled], false) # non-local non-whitelisted URLs through it and be sure that body size constraint is preserved. def preview_enabled?, do: enabled?() and !!Config.get([:media_preview_proxy, :enabled]) - def local?(url), do: String.starts_with?(url, Pleroma.Web.base_url()) + def local?(url), do: String.starts_with?(url, Web.base_url()) def whitelisted?(url) do %{host: domain} = URI.parse(url) @@ -75,17 +77,10 @@ def whitelisted?(url) do mediaproxy_whitelist_domains = [:media_proxy, :whitelist] |> Config.get() + |> Kernel.++(["#{Upload.base_url()}"]) |> Enum.map(&maybe_get_domain_from_url/1) - whitelist_domains = - if base_url = Config.get([Upload, :base_url]) do - %{host: base_domain} = URI.parse(base_url) - [base_domain | mediaproxy_whitelist_domains] - else - mediaproxy_whitelist_domains - end - - domain in whitelist_domains + domain in mediaproxy_whitelist_domains end defp maybe_get_domain_from_url("http" <> _ = url) do @@ -126,6 +121,11 @@ def decode_url(sig, url) do end end + def decode_url(encoded) do + [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") + decode_url(sig, base64) + end + defp signed_url(url) do :crypto.hmac(:sha, Config.get([Web.Endpoint, :secret_key_base]), url) end diff --git a/lib/pleroma/web/media_proxy/invalidation.ex b/lib/pleroma/web/media_proxy/invalidation.ex index 4f4340478..cb2db5ce9 100644 --- a/lib/pleroma/web/media_proxy/invalidation.ex +++ b/lib/pleroma/web/media_proxy/invalidation.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy.Invalidation do diff --git a/lib/pleroma/web/media_proxy/invalidation/http.ex b/lib/pleroma/web/media_proxy/invalidation/http.ex index 0b0cde68c..0b2a45518 100644 --- a/lib/pleroma/web/media_proxy/invalidation/http.ex +++ b/lib/pleroma/web/media_proxy/invalidation/http.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy.Invalidation.Http do diff --git a/lib/pleroma/web/media_proxy/invalidation/script.ex b/lib/pleroma/web/media_proxy/invalidation/script.ex index d32ffc50b..87a21166c 100644 --- a/lib/pleroma/web/media_proxy/invalidation/script.ex +++ b/lib/pleroma/web/media_proxy/invalidation/script.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy.Invalidation.Script do @@ -13,6 +13,7 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.Script do def purge(urls, opts \\ []) do args = urls + |> maybe_format_urls(Keyword.get(opts, :url_format)) |> List.wrap() |> Enum.uniq() |> Enum.join(" ") @@ -40,4 +41,22 @@ defp handle_result(error, _) do Logger.error("Error while cache purge: #{inspect(error)}") {:error, inspect(error)} end + + def maybe_format_urls(urls, :htcacheclean) do + urls + |> Enum.map(fn url -> + uri = URI.parse(url) + + query = + if !is_nil(uri.query) do + "?" <> uri.query + else + "?" + end + + uri.scheme <> "://" <> uri.host <> ":#{inspect(uri.port)}" <> uri.path <> query + end) + end + + def maybe_format_urls(urls, _), do: urls end diff --git a/lib/pleroma/web/media_proxy/media_proxy_controller.ex b/lib/pleroma/web/media_proxy/media_proxy_controller.ex index 90651ed9b..c74eaaf93 100644 --- a/lib/pleroma/web/media_proxy/media_proxy_controller.ex +++ b/lib/pleroma/web/media_proxy/media_proxy_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy.MediaProxyController do diff --git a/lib/pleroma/web/metadata.ex b/lib/pleroma/web/metadata.ex index 0f2d8d1e7..46ef00c08 100644 --- a/lib/pleroma/web/metadata.ex +++ b/lib/pleroma/web/metadata.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata do diff --git a/lib/pleroma/web/metadata/player_view.ex b/lib/pleroma/web/metadata/player_view.ex index 5a918532a..9be5e433d 100644 --- a/lib/pleroma/web/metadata/player_view.ex +++ b/lib/pleroma/web/metadata/player_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.PlayerView do diff --git a/lib/pleroma/web/metadata/providers/feed.ex b/lib/pleroma/web/metadata/providers/feed.ex index bd1459a17..d0ab5c19e 100644 --- a/lib/pleroma/web/metadata/providers/feed.ex +++ b/lib/pleroma/web/metadata/providers/feed.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.Feed do diff --git a/lib/pleroma/web/metadata/providers/open_graph.ex b/lib/pleroma/web/metadata/providers/open_graph.ex index bb1b23208..1687b2634 100644 --- a/lib/pleroma/web/metadata/providers/open_graph.ex +++ b/lib/pleroma/web/metadata/providers/open_graph.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.OpenGraph do diff --git a/lib/pleroma/web/metadata/providers/provider.ex b/lib/pleroma/web/metadata/providers/provider.ex index 767288f9c..c91d87c6d 100644 --- a/lib/pleroma/web/metadata/providers/provider.ex +++ b/lib/pleroma/web/metadata/providers/provider.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.Provider do diff --git a/lib/pleroma/web/metadata/providers/rel_me.ex b/lib/pleroma/web/metadata/providers/rel_me.ex index 8905c9c72..f013def51 100644 --- a/lib/pleroma/web/metadata/providers/rel_me.ex +++ b/lib/pleroma/web/metadata/providers/rel_me.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.RelMe do diff --git a/lib/pleroma/web/metadata/providers/restrict_indexing.ex b/lib/pleroma/web/metadata/providers/restrict_indexing.ex index a08a04b4a..aa6511610 100644 --- a/lib/pleroma/web/metadata/providers/restrict_indexing.ex +++ b/lib/pleroma/web/metadata/providers/restrict_indexing.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.RestrictIndexing do diff --git a/lib/pleroma/web/metadata/providers/twitter_card.ex b/lib/pleroma/web/metadata/providers/twitter_card.ex index df34b033f..58fc05cf9 100644 --- a/lib/pleroma/web/metadata/providers/twitter_card.ex +++ b/lib/pleroma/web/metadata/providers/twitter_card.ex @@ -1,6 +1,6 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.TwitterCard do diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex index 8a206e019..de7195435 100644 --- a/lib/pleroma/web/metadata/utils.ex +++ b/lib/pleroma/web/metadata/utils.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Utils do diff --git a/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex b/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex index 2a5c7c356..6ace3e0b5 100644 --- a/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex +++ b/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MongooseIM.MongooseIMController do @@ -14,7 +14,7 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do plug(RateLimiter, [name: :authentication, params: ["user"]] when action == :check_password) def user_exists(conn, %{"user" => username}) do - with %User{} <- Repo.get_by(User, nickname: username, local: true, deactivated: false) do + with %User{} <- Repo.get_by(User, nickname: username, local: true, is_active: true) do conn |> json(true) else @@ -26,7 +26,7 @@ def user_exists(conn, %{"user" => username}) do end def check_password(conn, %{"user" => username, "pass" => password}) do - with %User{password_hash: password_hash, deactivated: false} <- + with %User{password_hash: password_hash, is_active: true} <- Repo.get_by(User, nickname: username, local: true), true <- AuthenticationPlug.checkpw(password, password_hash) do conn diff --git a/lib/pleroma/web/nodeinfo/nodeinfo.ex b/lib/pleroma/web/nodeinfo/nodeinfo.ex index 47fa46376..6a0112d2a 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Nodeinfo.Nodeinfo do diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex index 8c7a9e565..bca94d236 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Nodeinfo.NodeinfoController do diff --git a/lib/pleroma/web/o_auth.ex b/lib/pleroma/web/o_auth.ex index 2f1b8708d..3bc1a6ad4 100644 --- a/lib/pleroma/web/o_auth.ex +++ b/lib/pleroma/web/o_auth.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth do diff --git a/lib/pleroma/web/o_auth/app.ex b/lib/pleroma/web/o_auth/app.ex index df99472e1..382750010 100644 --- a/lib/pleroma/web/o_auth/app.ex +++ b/lib/pleroma/web/o_auth/app.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.App do diff --git a/lib/pleroma/web/o_auth/authorization.ex b/lib/pleroma/web/o_auth/authorization.ex index e766dcada..e0ecb0f4f 100644 --- a/lib/pleroma/web/o_auth/authorization.ex +++ b/lib/pleroma/web/o_auth/authorization.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Authorization do diff --git a/lib/pleroma/web/o_auth/fallback_controller.ex b/lib/pleroma/web/o_auth/fallback_controller.ex index a89ced886..df68cbfc1 100644 --- a/lib/pleroma/web/o_auth/fallback_controller.ex +++ b/lib/pleroma/web/o_auth/fallback_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.FallbackController do diff --git a/lib/pleroma/web/o_auth/mfa_controller.ex b/lib/pleroma/web/o_auth/mfa_controller.ex index 5d5ec286a..b38b00213 100644 --- a/lib/pleroma/web/o_auth/mfa_controller.ex +++ b/lib/pleroma/web/o_auth/mfa_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.MFAController do diff --git a/lib/pleroma/web/o_auth/mfa_view.ex b/lib/pleroma/web/o_auth/mfa_view.ex index 5d87db268..3d473f29c 100644 --- a/lib/pleroma/web/o_auth/mfa_view.ex +++ b/lib/pleroma/web/o_auth/mfa_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.MFAView do diff --git a/lib/pleroma/web/o_auth/o_auth_controller.ex b/lib/pleroma/web/o_auth/o_auth_controller.ex index 6e3c7e1a1..215d97b3a 100644 --- a/lib/pleroma/web/o_auth/o_auth_controller.ex +++ b/lib/pleroma/web/o_auth/o_auth_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.OAuthController do diff --git a/lib/pleroma/web/o_auth/o_auth_view.ex b/lib/pleroma/web/o_auth/o_auth_view.ex index d22b2f7fe..1419c96a2 100644 --- a/lib/pleroma/web/o_auth/o_auth_view.ex +++ b/lib/pleroma/web/o_auth/o_auth_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.OAuthView do @@ -10,6 +10,7 @@ defmodule Pleroma.Web.OAuth.OAuthView do def render("token.json", %{token: token} = opts) do response = %{ + id: token.id, token_type: "Bearer", access_token: token.token, refresh_token: token.refresh_token, diff --git a/lib/pleroma/web/o_auth/scopes.ex b/lib/pleroma/web/o_auth/scopes.ex index 90b9a0471..ada43eae9 100644 --- a/lib/pleroma/web/o_auth/scopes.ex +++ b/lib/pleroma/web/o_auth/scopes.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Scopes do diff --git a/lib/pleroma/web/o_auth/token.ex b/lib/pleroma/web/o_auth/token.ex index 886117d15..9d69e9db4 100644 --- a/lib/pleroma/web/o_auth/token.ex +++ b/lib/pleroma/web/o_auth/token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Token do diff --git a/lib/pleroma/web/o_auth/token/query.ex b/lib/pleroma/web/o_auth/token/query.ex index fd6d9b112..d16a759d8 100644 --- a/lib/pleroma/web/o_auth/token/query.ex +++ b/lib/pleroma/web/o_auth/token/query.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Token.Query do diff --git a/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex b/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex index 625b0fde2..f5a0ed272 100644 --- a/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex +++ b/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Token.Strategy.RefreshToken do diff --git a/lib/pleroma/web/o_auth/token/strategy/revoke.ex b/lib/pleroma/web/o_auth/token/strategy/revoke.ex index 069c1ee21..8d6572704 100644 --- a/lib/pleroma/web/o_auth/token/strategy/revoke.ex +++ b/lib/pleroma/web/o_auth/token/strategy/revoke.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Token.Strategy.Revoke do diff --git a/lib/pleroma/web/o_auth/token/utils.ex b/lib/pleroma/web/o_auth/token/utils.ex index 43aeab6b0..b572dc9cf 100644 --- a/lib/pleroma/web/o_auth/token/utils.ex +++ b/lib/pleroma/web/o_auth/token/utils.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Token.Utils do diff --git a/lib/pleroma/web/o_status/o_status_controller.ex b/lib/pleroma/web/o_status/o_status_controller.ex index 668ae0ea4..da3264149 100644 --- a/lib/pleroma/web/o_status/o_status_controller.ex +++ b/lib/pleroma/web/o_status/o_status_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OStatus.OStatusController do @@ -73,15 +73,11 @@ def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do cond do format in ["json", "activity+json"] -> - if activity.local do - %{data: %{"id" => redirect_url}} = Object.normalize(activity) - redirect(conn, external: redirect_url) - else - {:error, :not_found} - end + %{data: %{"id" => redirect_url}} = Object.normalize(activity, fetch: false) + redirect(conn, external: redirect_url) activity.data["type"] == "Create" -> - %Object{} = object = Object.normalize(activity) + %Object{} = object = Object.normalize(activity, fetch: false) RedirectController.redirector_with_meta( conn, @@ -112,7 +108,7 @@ def notice_player(conn, %{"id" => id}) do with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.is_public?(activity), {_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)}, - %Object{} = object <- Object.normalize(activity), + %Object{} = object <- Object.normalize(activity, fetch: false), %{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object, true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do conn diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index 30cf83567..165afd3b4 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.AccountController do @@ -56,7 +56,7 @@ def confirmation_resend(conn, params) do nickname_or_email = params[:email] || params[:nickname] with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email), - {:ok, _} <- User.try_send_confirmation_email(user) do + {:ok, _} <- User.maybe_send_confirmation_email(user) do json_response(conn, :no_content, "") end end diff --git a/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex b/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex index dd0a2e22f..fc5d16771 100644 --- a/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.BackupController do @@ -10,7 +10,7 @@ defmodule Pleroma.Web.PleromaAPI.BackupController do action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action in [:index, :create]) - plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError) + plug(Pleroma.Web.ApiSpec.CastAndValidate) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaBackupOperation diff --git a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex index bfc0a1f19..dcd54b1af 100644 --- a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ChatController do use Pleroma.Web, :controller @@ -35,10 +35,10 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do plug( OAuthScopesPlug, - %{scopes: ["read:chats"]} when action in [:messages, :index, :show] + %{scopes: ["read:chats"]} when action in [:messages, :index, :index2, :show] ) - plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError) + plug(Pleroma.Web.ApiSpec.CastAndValidate) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ChatOperation @@ -82,7 +82,7 @@ def post_chat_message( media_id: params[:media_id], idempotency_key: idempotency_key(conn) ), - message <- Object.normalize(activity, false), + message <- Object.normalize(activity, fetch: false), cm_ref <- MessageReference.for_chat_and_object(chat, message) do conn |> put_view(MessageReferenceView) @@ -138,18 +138,30 @@ def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do end end - def index(%{assigns: %{user: %{id: user_id} = user}} = conn, params) do + def index(%{assigns: %{user: user}} = conn, params) do + chats = + index_query(user, params) + |> Repo.all() + + render(conn, "index.json", chats: chats) + end + + def index2(%{assigns: %{user: user}} = conn, params) do + chats = + index_query(user, params) + |> Pagination.fetch_paginated(params) + + render(conn, "index.json", chats: chats) + end + + defp index_query(%{id: user_id} = user, params) do exclude_users = User.cached_blocked_users_ap_ids(user) ++ if params[:with_muted], do: [], else: User.cached_muted_users_ap_ids(user) - chats = - user_id - |> Chat.for_user_query() - |> where([c], c.recipient not in ^exclude_users) - |> Repo.all() - - render(conn, "index.json", chats: chats) + user_id + |> Chat.for_user_query() + |> where([c], c.recipient not in ^exclude_users) end def create(%{assigns: %{user: user}} = conn, %{id: id}) do diff --git a/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex b/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex index df52b7566..d285e4907 100644 --- a/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ConversationController do diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex index 428c97de6..204e81311 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.EmojiFileController do @@ -12,7 +12,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do plug( Pleroma.Web.Plugs.OAuthScopesPlug, - %{scopes: ["write"], admin: true} + %{scopes: ["admin:write"]} when action in [ :create, :update, @@ -42,7 +42,10 @@ def create(%{body_params: params} = conn, %{name: pack_name}) do |> json(%{error: "pack name, shortcode or filename cannot be empty"}) {:error, _} = error -> - handle_error(conn, error, %{pack_name: pack_name}) + handle_error(conn, error, %{ + pack_name: pack_name, + message: "Unexpected error occurred while adding file to pack." + }) end end @@ -69,7 +72,11 @@ def update(%{body_params: %{shortcode: shortcode} = params} = conn, %{name: pack |> json(%{error: "new_shortcode or new_filename cannot be empty"}) {:error, _} = error -> - handle_error(conn, error, %{pack_name: pack_name, code: shortcode}) + handle_error(conn, error, %{ + pack_name: pack_name, + code: shortcode, + message: "Unexpected error occurred while updating." + }) end end @@ -84,7 +91,11 @@ def delete(conn, %{name: pack_name, shortcode: shortcode}) do |> json(%{error: "pack name or shortcode cannot be empty"}) {:error, _} = error -> - handle_error(conn, error, %{pack_name: pack_name, code: shortcode}) + handle_error(conn, error, %{ + pack_name: pack_name, + code: shortcode, + message: "Unexpected error occurred while deleting emoji file." + }) end end @@ -94,18 +105,24 @@ defp handle_error(conn, {:error, :doesnt_exist}, %{code: emoji_code}) do |> json(%{error: "Emoji \"#{emoji_code}\" does not exist"}) end - defp handle_error(conn, {:error, :not_found}, %{pack_name: pack_name}) do + defp handle_error(conn, {:error, :enoent}, %{pack_name: pack_name}) do conn |> put_status(:not_found) |> json(%{error: "pack \"#{pack_name}\" is not found"}) end - defp handle_error(conn, {:error, _}, _) do - render_error( - conn, - :internal_server_error, - "Unexpected error occurred while adding file to pack." - ) + defp handle_error(conn, {:error, error}, opts) do + message = + [ + Map.get(opts, :message, "Unexpected error occurred."), + Pleroma.Utils.posix_error_message(error) + ] + |> Enum.join(" ") + |> String.trim() + + conn + |> put_status(:internal_server_error) + |> json(%{error: message}) end defp get_filename(%Plug.Upload{filename: filename}), do: filename diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex index a9accc5af..d0f677d3c 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.EmojiPackController do @@ -11,7 +11,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do plug( Pleroma.Web.Plugs.OAuthScopesPlug, - %{scopes: ["write"], admin: true} + %{scopes: ["admin:write"]} when action in [ :import_from_filesystem, :remote, @@ -71,7 +71,7 @@ def show(conn, %{name: name, page: page, page_size: page_size}) do with {:ok, pack} <- Pack.show(name: name, page: page, page_size: page_size) do json(conn, pack) else - {:error, :not_found} -> + {:error, :enoent} -> conn |> put_status(:not_found) |> json(%{error: "Pack #{name} does not exist"}) @@ -80,6 +80,17 @@ def show(conn, %{name: name, page: page, page_size: page_size}) do conn |> put_status(:bad_request) |> json(%{error: "pack name cannot be empty"}) + + {:error, error} -> + error_message = + add_posix_error( + "Failed to get the contents of the `#{name}` pack.", + error + ) + + conn + |> put_status(:internal_server_error) + |> json(%{error: error_message}) end end @@ -95,7 +106,7 @@ def archive(conn, %{name: name}) do "Pack #{name} cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing" }) - {:error, :not_found} -> + {:error, :enoent} -> conn |> put_status(:not_found) |> json(%{error: "Pack #{name} does not exist"}) @@ -116,10 +127,10 @@ def download(%{body_params: %{url: url, name: name} = params} = conn, _) do |> put_status(:internal_server_error) |> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"}) - {:error, e} -> + {:error, error} -> conn |> put_status(:internal_server_error) - |> json(%{error: e}) + |> json(%{error: error}) end end @@ -139,12 +150,16 @@ def create(conn, %{name: name}) do |> put_status(:bad_request) |> json(%{error: "pack name cannot be empty"}) - {:error, _} -> - render_error( - conn, - :internal_server_error, - "Unexpected error occurred while creating pack." - ) + {:error, error} -> + error_message = + add_posix_error( + "Unexpected error occurred while creating pack.", + error + ) + + conn + |> put_status(:internal_server_error) + |> json(%{error: error_message}) end end @@ -164,10 +179,12 @@ def delete(conn, %{name: name}) do |> put_status(:bad_request) |> json(%{error: "pack name cannot be empty"}) - {:error, _, _} -> + {:error, error, _} -> + error_message = add_posix_error("Couldn't delete the `#{name}` pack", error) + conn |> put_status(:internal_server_error) - |> json(%{error: "Couldn't delete the pack #{name}"}) + |> json(%{error: error_message}) end end @@ -180,12 +197,16 @@ def update(%{body_params: %{metadata: metadata}} = conn, %{name: name}) do |> put_status(:bad_request) |> json(%{error: "The fallback archive does not have all files specified in pack.json"}) - {:error, _} -> - render_error( - conn, - :internal_server_error, - "Unexpected error occurred while updating pack metadata." - ) + {:error, error} -> + error_message = + add_posix_error( + "Unexpected error occurred while updating pack metadata.", + error + ) + + conn + |> put_status(:internal_server_error) + |> json(%{error: error_message}) end end @@ -204,4 +225,10 @@ def import_from_filesystem(conn, _params) do |> json(%{error: "Error accessing emoji pack directory"}) end end + + defp add_posix_error(msg, error) do + [msg, Pleroma.Utils.posix_error_message(error)] + |> Enum.join(" ") + |> String.trim() + end end diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex index dd9c746dc..da5f2474f 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do @@ -29,7 +29,7 @@ def index(%{assigns: %{user: user}} = conn, %{id: activity_id} = params) do with true <- Pleroma.Config.get([:instance, :show_reactions]), %Activity{} = activity <- Activity.get_by_id_with_object(activity_id), %Object{data: %{"reactions" => reactions}} when is_list(reactions) <- - Object.normalize(activity) do + Object.normalize(activity, fetch: false) do reactions = reactions |> filter(params) diff --git a/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex index 9e97480df..01424c6ba 100644 --- a/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.InstancesController do diff --git a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex index 15210f1e6..429ef5112 100644 --- a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.MascotController do diff --git a/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex b/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex index fa32aaa84..257bcd550 100644 --- a/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.NotificationController do diff --git a/lib/pleroma/web/pleroma_api/controllers/report_controller.ex b/lib/pleroma/web/pleroma_api/controllers/report_controller.ex new file mode 100644 index 000000000..d93d7570a --- /dev/null +++ b/lib/pleroma/web/pleroma_api/controllers/report_controller.ex @@ -0,0 +1,46 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.ReportController do + use Pleroma.Web, :controller + + alias Pleroma.Activity + alias Pleroma.Web.ActivityPub.Utils + alias Pleroma.Web.AdminAPI.Report + + action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read:reports"]}) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaReportOperation + + @doc "GET /api/v0/pleroma/reports" + def index(%{assigns: %{user: user}, body_params: params} = conn, _) do + params = + params + |> Map.put(:actor_id, user.ap_id) + + reports = Utils.get_reports(params, Map.get(params, :page, 1), Map.get(params, :size, 20)) + + render(conn, "index.json", %{reports: reports, for: user}) + end + + @doc "GET /api/v0/pleroma/reports/:id" + def show(%{assigns: %{user: user}} = conn, %{id: id}) do + with %Activity{} = report <- Activity.get_report(id), + true <- report.actor == user.ap_id, + %{} = report_info <- Report.extract_report_info(report) do + render(conn, "show.json", Map.put(report_info, :for, user)) + else + false -> + {:error, :not_found} + + nil -> + {:error, :not_found} + + e -> + {:error, inspect(e)} + end + end +end diff --git a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex index 632d65434..ca26d80ef 100644 --- a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ScrobbleController do diff --git a/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex b/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex index eba452300..3940ad581 100644 --- a/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationController do diff --git a/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex b/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex index 7f089af1c..078d470d9 100644 --- a/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.UserImportController do @@ -15,7 +15,7 @@ defmodule Pleroma.Web.PleromaAPI.UserImportController do plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks) plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action == :mutes) - plug(OpenApiSpex.Plug.CastAndValidate) + plug(Pleroma.Web.ApiSpec.CastAndValidate) defdelegate open_api_operation(action), to: ApiSpec.UserImportOperation def follow(%{body_params: %{list: %Plug.Upload{path: path}}} = conn, _) do diff --git a/lib/pleroma/web/pleroma_api/views/backup_view.ex b/lib/pleroma/web/pleroma_api/views/backup_view.ex index af75876aa..944600c86 100644 --- a/lib/pleroma/web/pleroma_api/views/backup_view.ex +++ b/lib/pleroma/web/pleroma_api/views/backup_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.BackupView do @@ -10,6 +10,7 @@ defmodule Pleroma.Web.PleromaAPI.BackupView do def render("show.json", %{backup: %Backup{} = backup}) do %{ + id: backup.id, content_type: backup.content_type, url: download_url(backup), file_size: backup.file_size, @@ -23,6 +24,6 @@ def render("index.json", %{backups: backups}) do end def download_url(%Backup{file_name: file_name}) do - Pleroma.Web.Endpoint.url() <> "/media/backups/" <> file_name + Pleroma.Upload.base_url() <> "/backups/" <> file_name end end diff --git a/lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex b/lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex index c058fb340..2e4355992 100644 --- a/lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex +++ b/lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do @@ -10,6 +10,8 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.MastodonAPI.StatusView + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + def render( "show.json", %{ @@ -51,7 +53,7 @@ def render("index.json", opts) do end defp put_idempotency_key(data) do - with {:ok, idempotency_key} <- Cachex.get(:chat_message_id_idempotency_key_cache, data.id) do + with {:ok, idempotency_key} <- @cachex.get(:chat_message_id_idempotency_key_cache, data.id) do data |> Maps.put_if_present(:idempotency_key, idempotency_key) else diff --git a/lib/pleroma/web/pleroma_api/views/chat_view.ex b/lib/pleroma/web/pleroma_api/views/chat_view.ex index 04dc20d51..3794818a7 100644 --- a/lib/pleroma/web/pleroma_api/views/chat_view.ex +++ b/lib/pleroma/web/pleroma_api/views/chat_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ChatView do diff --git a/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex b/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex index 110e8a041..c94527e6d 100644 --- a/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex +++ b/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.EmojiReactionView do @@ -26,7 +26,7 @@ defp fetch_users(user_ap_ids) do user_ap_ids |> Enum.map(&Pleroma.User.get_cached_by_ap_id/1) |> Enum.filter(fn - %{deactivated: false} -> true + %{is_active: true} -> true _ -> false end) end diff --git a/lib/pleroma/web/pleroma_api/views/report_view.ex b/lib/pleroma/web/pleroma_api/views/report_view.ex new file mode 100644 index 000000000..a0b3f085c --- /dev/null +++ b/lib/pleroma/web/pleroma_api/views/report_view.ex @@ -0,0 +1,55 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.ReportView do + use Pleroma.Web, :view + + alias Pleroma.HTML + alias Pleroma.Web.AdminAPI.Report + alias Pleroma.Web.CommonAPI.Utils + alias Pleroma.Web.MastodonAPI.AccountView + alias Pleroma.Web.MastodonAPI.StatusView + + def render("index.json", %{reports: reports, for: for_user}) do + %{ + reports: + reports[:items] + |> Enum.map(&Report.extract_report_info/1) + |> Enum.map(&render(__MODULE__, "show.json", Map.put(&1, :for, for_user))), + total: reports[:total] + } + end + + def render("show.json", %{ + report: report, + user: actor, + account: account, + statuses: statuses, + for: for_user + }) do + created_at = Utils.to_masto_date(report.data["published"]) + + content = + unless is_nil(report.data["content"]) do + HTML.filter_tags(report.data["content"]) + else + nil + end + + %{ + id: report.id, + account: AccountView.render("show.json", %{user: account, for: for_user}), + actor: AccountView.render("show.json", %{user: actor, for: for_user}), + content: content, + created_at: created_at, + statuses: + StatusView.render("index.json", %{ + activities: statuses, + as: :activity, + for: for_user + }), + state: report.data["state"] + } + end +end diff --git a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex index 95bd4c368..2bc069529 100644 --- a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex +++ b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ScrobbleView do @@ -15,7 +15,7 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleView do alias Pleroma.Web.MastodonAPI.AccountView def render("show.json", %{activity: %Activity{data: %{"type" => "Listen"}} = activity} = opts) do - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) user = CommonAPI.get_user(activity.data["actor"]) created_at = Utils.to_masto_date(activity.data["published"]) diff --git a/lib/pleroma/web/plug.ex b/lib/pleroma/web/plug.ex index 840b35072..dffad3a06 100644 --- a/lib/pleroma/web/plug.ex +++ b/lib/pleroma/web/plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plug do diff --git a/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex b/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex index ff851a874..976e5cd92 100644 --- a/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex +++ b/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlug do diff --git a/lib/pleroma/web/plugs/authentication_plug.ex b/lib/pleroma/web/plugs/authentication_plug.ex index a7b8a9bfe..8d58169cf 100644 --- a/lib/pleroma/web/plugs/authentication_plug.ex +++ b/lib/pleroma/web/plugs/authentication_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.AuthenticationPlug do @@ -48,7 +48,7 @@ def checkpw(password, "$2" <> _ = password_hash) do end def checkpw(password, "$pbkdf2" <> _ = password_hash) do - Pbkdf2.verify_pass(password, password_hash) + Pleroma.Password.Pbkdf2.verify_pass(password, password_hash) end def checkpw(_password, _password_hash) do diff --git a/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex b/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex index 97529aedb..397f26de5 100644 --- a/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex +++ b/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.BasicAuthDecoderPlug do diff --git a/lib/pleroma/web/plugs/cache.ex b/lib/pleroma/web/plugs/cache.ex index 6de01804a..111854859 100644 --- a/lib/pleroma/web/plugs/cache.ex +++ b/lib/pleroma/web/plugs/cache.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.Cache do @@ -41,6 +41,8 @@ def index(conn, _params) do @defaults %{ttl: nil, query_params: true} + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + @impl true def init([]), do: @defaults @@ -53,7 +55,7 @@ def init(opts) do def call(%{method: "GET"} = conn, opts) do key = cache_key(conn, opts) - case Cachex.get(:web_resp_cache, key) do + case @cachex.get(:web_resp_cache, key) do {:ok, nil} -> cache_resp(conn, opts) @@ -97,11 +99,11 @@ defp cache_resp(conn, opts) do conn = unless opts[:tracking_fun] do - Cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl) + @cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl) conn else tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil) - Cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl) + @cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl) opts.tracking_fun.(conn, tracking_fun_data) end diff --git a/lib/pleroma/web/plugs/digest_plug.ex b/lib/pleroma/web/plugs/digest_plug.ex index fb2723b97..d72f8073c 100644 --- a/lib/pleroma/web/plugs/digest_plug.ex +++ b/lib/pleroma/web/plugs/digest_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.DigestPlug do diff --git a/lib/pleroma/web/plugs/ensure_authenticated_plug.ex b/lib/pleroma/web/plugs/ensure_authenticated_plug.ex index ea2af6881..31e7410d6 100644 --- a/lib/pleroma/web/plugs/ensure_authenticated_plug.ex +++ b/lib/pleroma/web/plugs/ensure_authenticated_plug.ex @@ -1,8 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.EnsureAuthenticatedPlug do + @moduledoc """ + Ensures _user_ authentication (app-bound user-unbound tokens are not accepted). + """ + import Plug.Conn import Pleroma.Web.TranslationHelpers diff --git a/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex b/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex index 3bebdac6d..8a8532f41 100644 --- a/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex +++ b/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex @@ -1,8 +1,13 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug do + @moduledoc """ + Ensures instance publicity or _user_ authentication + (app-bound user-unbound tokens are accepted only if the instance is public). + """ + import Pleroma.Web.TranslationHelpers import Plug.Conn diff --git a/lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex b/lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex index 4253458b2..534b0cff1 100644 --- a/lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex +++ b/lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug do @@ -28,6 +28,11 @@ def call(%{assigns: %{user: %User{id: user_id}} = assigns} = conn, _) do end end + # App-bound token case (obtained with client_id and client_secret) + def call(%{assigns: %{token: %Token{user_id: nil}}} = conn, _) do + assign(conn, :user, nil) + end + def call(conn, _) do conn |> assign(:user, nil) diff --git a/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex b/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex index 0925ded4d..f09cffe95 100644 --- a/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex +++ b/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug do diff --git a/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex b/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex index ace512a78..e227d5150 100644 --- a/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex +++ b/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug do diff --git a/lib/pleroma/web/plugs/federating_plug.ex b/lib/pleroma/web/plugs/federating_plug.ex index 3c90a7644..eeef7e45b 100644 --- a/lib/pleroma/web/plugs/federating_plug.ex +++ b/lib/pleroma/web/plugs/federating_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.FederatingPlug do diff --git a/lib/pleroma/web/plugs/frontend_static.ex b/lib/pleroma/web/plugs/frontend_static.ex index 1b0b36813..eb385e94d 100644 --- a/lib/pleroma/web/plugs/frontend_static.ex +++ b/lib/pleroma/web/plugs/frontend_static.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.FrontendStatic do @@ -10,6 +10,8 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do """ @behaviour Plug + @api_routes Pleroma.Web.get_api_routes() + def file_path(path, frontend_type \\ :primary) do if configuration = Pleroma.Config.get([:frontends, frontend_type]) do instance_static_path = Pleroma.Config.get([:instance, :static_dir], "instance/static") @@ -34,7 +36,8 @@ def init(opts) do end def call(conn, opts) do - with false <- invalid_path?(conn.path_info), + with false <- api_route?(conn.path_info), + false <- invalid_path?(conn.path_info), frontend_type <- Map.get(opts, :frontend_type, :primary), path when not is_nil(path) <- file_path("", frontend_type) do call_static(conn, opts, path) @@ -52,6 +55,10 @@ defp invalid_path?([h | _], _match) when h in [".", "..", ""], do: true defp invalid_path?([h | t], match), do: String.contains?(h, match) or invalid_path?(t) defp invalid_path?([], _match), do: false + defp api_route?([h | _]) when h in @api_routes, do: true + defp api_route?([_ | t]), do: api_route?(t) + defp api_route?([]), do: false + defp call_static(conn, opts, from) do opts = Map.put(opts, :from, from) Plug.Static.call(conn, opts) diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex index 45aaf188e..d1e6cc9d3 100644 --- a/lib/pleroma/web/plugs/http_security_plug.ex +++ b/lib/pleroma/web/plugs/http_security_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do @@ -20,9 +20,26 @@ def call(conn, _options) do end end - defp headers do + def primary_frontend do + with %{"name" => frontend} <- Config.get([:frontends, :primary]), + available <- Config.get([:frontends, :available]), + %{} = primary_frontend <- Map.get(available, frontend) do + {:ok, primary_frontend} + end + end + + def custom_http_frontend_headers do + with {:ok, %{"custom-http-headers" => custom_headers}} <- primary_frontend() do + custom_headers + else + _ -> [] + end + end + + def headers do referrer_policy = Config.get([:http_security, :referrer_policy]) report_uri = Config.get([:http_security, :report_uri]) + custom_http_frontend_headers = custom_http_frontend_headers() headers = [ {"x-xss-protection", "1; mode=block"}, @@ -31,9 +48,17 @@ defp headers do {"x-content-type-options", "nosniff"}, {"referrer-policy", referrer_policy}, {"x-download-options", "noopen"}, - {"content-security-policy", csp_string()} + {"content-security-policy", csp_string()}, + {"permissions-policy", "interest-cohort=()"} ] + headers = + if custom_http_frontend_headers do + custom_http_frontend_headers ++ headers + else + headers + end + if report_uri do report_group = %{ "group" => "csp-endpoint", diff --git a/lib/pleroma/web/plugs/http_signature_plug.ex b/lib/pleroma/web/plugs/http_signature_plug.ex index 036e2a773..0f7550516 100644 --- a/lib/pleroma/web/plugs/http_signature_plug.ex +++ b/lib/pleroma/web/plugs/http_signature_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do diff --git a/lib/pleroma/web/plugs/idempotency_plug.ex b/lib/pleroma/web/plugs/idempotency_plug.ex index 254a790b0..9ac8f3647 100644 --- a/lib/pleroma/web/plugs/idempotency_plug.ex +++ b/lib/pleroma/web/plugs/idempotency_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.IdempotencyPlug do @@ -8,6 +8,8 @@ defmodule Pleroma.Web.Plugs.IdempotencyPlug do @behaviour Plug + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + @impl true def init(opts), do: opts @@ -25,7 +27,7 @@ def call(%{method: method} = conn, _) when method in ["POST", "PUT", "PATCH"] do def call(conn, _), do: conn def process_request(conn, key) do - case Cachex.get(:idempotency_cache, key) do + case @cachex.get(:idempotency_cache, key) do {:ok, nil} -> cache_resposnse(conn, key) @@ -43,7 +45,7 @@ defp cache_resposnse(conn, key) do content_type = get_content_type(conn) record = {request_id, content_type, conn.status, conn.resp_body} - {:ok, _} = Cachex.put(:idempotency_cache, key, record) + {:ok, _} = @cachex.put(:idempotency_cache, key, record) conn |> put_resp_header("idempotency-key", key) diff --git a/lib/pleroma/web/plugs/instance_static.ex b/lib/pleroma/web/plugs/instance_static.ex index 54b9175df..723b25679 100644 --- a/lib/pleroma/web/plugs/instance_static.ex +++ b/lib/pleroma/web/plugs/instance_static.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.InstanceStatic do diff --git a/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex index a0a0c5a9b..58cb0316a 100644 --- a/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex +++ b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do diff --git a/lib/pleroma/web/plugs/o_auth_plug.ex b/lib/pleroma/web/plugs/o_auth_plug.ex index eb287318b..5e06ac3f6 100644 --- a/lib/pleroma/web/plugs/o_auth_plug.ex +++ b/lib/pleroma/web/plugs/o_auth_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.OAuthPlug do diff --git a/lib/pleroma/web/plugs/o_auth_scopes_plug.ex b/lib/pleroma/web/plugs/o_auth_scopes_plug.ex index e6d398b14..f017c8bc7 100644 --- a/lib/pleroma/web/plugs/o_auth_scopes_plug.ex +++ b/lib/pleroma/web/plugs/o_auth_scopes_plug.ex @@ -1,12 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.OAuthScopesPlug do import Plug.Conn import Pleroma.Web.Gettext - alias Pleroma.Config alias Pleroma.Helpers.AuthHelper use Pleroma.Web, :plug @@ -18,7 +17,6 @@ def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do op = options[:op] || :| token = assigns[:token] - scopes = transform_scopes(scopes, options) matched_scopes = (token && filter_descendants(scopes, token.scopes)) || [] cond do @@ -57,13 +55,4 @@ def filter_descendants(scopes, supported_scopes) do end ) end - - @doc "Transforms scopes by applying supported options (e.g. :admin)" - def transform_scopes(scopes, options) do - if options[:admin] do - Config.oauth_admin_scopes(scopes) - else - scopes - end - end end diff --git a/lib/pleroma/web/plugs/plug_helper.ex b/lib/pleroma/web/plugs/plug_helper.ex index b314e7596..d73021bf7 100644 --- a/lib/pleroma/web/plugs/plug_helper.ex +++ b/lib/pleroma/web/plugs/plug_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.PlugHelper do diff --git a/lib/pleroma/web/plugs/rate_limiter.ex b/lib/pleroma/web/plugs/rate_limiter.ex index a589610d1..5bebe0ad5 100644 --- a/lib/pleroma/web/plugs/rate_limiter.ex +++ b/lib/pleroma/web/plugs/rate_limiter.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.RateLimiter do @@ -72,6 +72,8 @@ defmodule Pleroma.Web.Plugs.RateLimiter do require Logger + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + @doc false def init(plug_opts) do plug_opts @@ -124,7 +126,7 @@ def inspect_bucket(conn, bucket_name_root, plug_opts) do key_name = make_key_name(action_settings) limit = get_limits(action_settings) - case Cachex.get(bucket_name, key_name) do + case @cachex.get(bucket_name, key_name) do {:error, :no_cache} -> @inspect_bucket_not_found @@ -157,7 +159,7 @@ defp check_rate(action_settings) do key_name = make_key_name(action_settings) limit = get_limits(action_settings) - case Cachex.get_and_update(bucket_name, key_name, &increment_value(&1, limit)) do + case @cachex.get_and_update(bucket_name, key_name, &increment_value(&1, limit)) do {:commit, value} -> {:ok, value} diff --git a/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex b/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex index 5642bb205..3db59bf17 100644 --- a/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex +++ b/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.RateLimiter.LimiterSupervisor do diff --git a/lib/pleroma/web/plugs/rate_limiter/supervisor.ex b/lib/pleroma/web/plugs/rate_limiter/supervisor.ex index a1c84063d..0dc2aa71b 100644 --- a/lib/pleroma/web/plugs/rate_limiter/supervisor.ex +++ b/lib/pleroma/web/plugs/rate_limiter/supervisor.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.RateLimiter.Supervisor do diff --git a/lib/pleroma/web/plugs/remote_ip.ex b/lib/pleroma/web/plugs/remote_ip.ex index 401e2cbfa..4d7daca56 100644 --- a/lib/pleroma/web/plugs/remote_ip.ex +++ b/lib/pleroma/web/plugs/remote_ip.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.RemoteIp do diff --git a/lib/pleroma/web/plugs/set_format_plug.ex b/lib/pleroma/web/plugs/set_format_plug.ex index c16d2f81d..7ef88f305 100644 --- a/lib/pleroma/web/plugs/set_format_plug.ex +++ b/lib/pleroma/web/plugs/set_format_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.SetFormatPlug do diff --git a/lib/pleroma/web/plugs/set_locale_plug.ex b/lib/pleroma/web/plugs/set_locale_plug.ex index d9d24b93f..d77191cff 100644 --- a/lib/pleroma/web/plugs/set_locale_plug.ex +++ b/lib/pleroma/web/plugs/set_locale_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only # NOTE: this module is based on https://github.com/smeevil/set_locale diff --git a/lib/pleroma/web/plugs/set_user_session_id_plug.ex b/lib/pleroma/web/plugs/set_user_session_id_plug.ex index 9f4a6b6ac..a1cfa0915 100644 --- a/lib/pleroma/web/plugs/set_user_session_id_plug.ex +++ b/lib/pleroma/web/plugs/set_user_session_id_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.SetUserSessionIdPlug do diff --git a/lib/pleroma/web/plugs/static_fe_plug.ex b/lib/pleroma/web/plugs/static_fe_plug.ex index 658a1052e..9ba9dc5ff 100644 --- a/lib/pleroma/web/plugs/static_fe_plug.ex +++ b/lib/pleroma/web/plugs/static_fe_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.StaticFEPlug do diff --git a/lib/pleroma/web/plugs/trailing_format_plug.ex b/lib/pleroma/web/plugs/trailing_format_plug.ex index e3f57c14a..c5069ae0e 100644 --- a/lib/pleroma/web/plugs/trailing_format_plug.ex +++ b/lib/pleroma/web/plugs/trailing_format_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.TrailingFormatPlug do diff --git a/lib/pleroma/web/plugs/uploaded_media.ex b/lib/pleroma/web/plugs/uploaded_media.ex index 402a8bb34..2378e98d2 100644 --- a/lib/pleroma/web/plugs/uploaded_media.ex +++ b/lib/pleroma/web/plugs/uploaded_media.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UploadedMedia do @@ -62,7 +62,7 @@ def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do def call(conn, _opts), do: conn defp media_is_banned(%{request_path: path} = _conn, {:static_dir, _}) do - MediaProxy.in_banned_urls(Pleroma.Web.base_url() <> path) + MediaProxy.in_banned_urls(Pleroma.Upload.base_url() <> path) end defp media_is_banned(_, {:url, url}), do: MediaProxy.in_banned_urls(url) @@ -87,8 +87,15 @@ defp get_media(conn, {:static_dir, directory}, _, opts) do end defp get_media(conn, {:url, url}, true, _) do + proxy_opts = [ + http: [ + follow_redirect: true, + pool: :upload + ] + ] + conn - |> Pleroma.ReverseProxy.call(url, Pleroma.Config.get([Pleroma.Upload, :proxy_opts], [])) + |> Pleroma.ReverseProxy.call(url, proxy_opts) end defp get_media(conn, {:url, url}, _, _) do diff --git a/lib/pleroma/web/plugs/user_enabled_plug.ex b/lib/pleroma/web/plugs/user_enabled_plug.ex index 4f1b163bd..1142a8dbc 100644 --- a/lib/pleroma/web/plugs/user_enabled_plug.ex +++ b/lib/pleroma/web/plugs/user_enabled_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UserEnabledPlug do diff --git a/lib/pleroma/web/plugs/user_fetcher_plug.ex b/lib/pleroma/web/plugs/user_fetcher_plug.ex index 89e16b49f..707df9bfd 100644 --- a/lib/pleroma/web/plugs/user_fetcher_plug.ex +++ b/lib/pleroma/web/plugs/user_fetcher_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UserFetcherPlug do diff --git a/lib/pleroma/web/plugs/user_is_admin_plug.ex b/lib/pleroma/web/plugs/user_is_admin_plug.ex index 531c965f0..7649912ba 100644 --- a/lib/pleroma/web/plugs/user_is_admin_plug.ex +++ b/lib/pleroma/web/plugs/user_is_admin_plug.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UserIsAdminPlug do diff --git a/lib/pleroma/web/plugs/user_tracking_plug.ex b/lib/pleroma/web/plugs/user_tracking_plug.ex new file mode 100644 index 000000000..c9a988f00 --- /dev/null +++ b/lib/pleroma/web/plugs/user_tracking_plug.ex @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.UserTrackingPlug do + alias Pleroma.User + + import Plug.Conn, only: [assign: 3] + + @update_interval :timer.hours(24) + + def init(opts), do: opts + + def call(%{assigns: %{user: %User{id: id} = user}} = conn, _) when not is_nil(id) do + with true <- needs_update?(user), + {:ok, user} <- User.update_last_active_at(user) do + assign(conn, :user, user) + else + _ -> conn + end + end + + def call(conn, _), do: conn + + defp needs_update?(%User{last_active_at: nil}), do: true + + defp needs_update?(%User{last_active_at: last_active_at}) do + NaiveDateTime.diff(NaiveDateTime.utc_now(), last_active_at, :millisecond) >= @update_interval + end +end diff --git a/lib/pleroma/web/preload.ex b/lib/pleroma/web/preload.ex index 90e454468..e8588bcc9 100644 --- a/lib/pleroma/web/preload.ex +++ b/lib/pleroma/web/preload.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload do diff --git a/lib/pleroma/web/preload/providers/instance.ex b/lib/pleroma/web/preload/providers/instance.ex index a549bb1eb..eb0254c74 100644 --- a/lib/pleroma/web/preload/providers/instance.ex +++ b/lib/pleroma/web/preload/providers/instance.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload.Providers.Instance do diff --git a/lib/pleroma/web/preload/providers/provider.ex b/lib/pleroma/web/preload/providers/provider.ex index 7ef595a34..60f304f2c 100644 --- a/lib/pleroma/web/preload/providers/provider.ex +++ b/lib/pleroma/web/preload/providers/provider.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload.Providers.Provider do diff --git a/lib/pleroma/web/preload/providers/timelines.ex b/lib/pleroma/web/preload/providers/timelines.ex index b279a865d..c1704ccdc 100644 --- a/lib/pleroma/web/preload/providers/timelines.ex +++ b/lib/pleroma/web/preload/providers/timelines.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload.Providers.Timelines do diff --git a/lib/pleroma/web/preload/providers/user.ex b/lib/pleroma/web/preload/providers/user.ex index b3d2e9b8d..504f79ba0 100644 --- a/lib/pleroma/web/preload/providers/user.ex +++ b/lib/pleroma/web/preload/providers/user.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload.Providers.User do diff --git a/lib/pleroma/web/push.ex b/lib/pleroma/web/push.ex index b80a6438d..154dae614 100644 --- a/lib/pleroma/web/push.ex +++ b/lib/pleroma/web/push.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Push do diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 82152dffa..83cbdc870 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Push.Impl do @@ -32,7 +32,7 @@ def perform( mastodon_type = notification.type gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key) avatar_url = User.avatar_url(actor) - object = Object.normalize(activity, false) + object = Object.normalize(activity, fetch: false) user = User.get_cached_by_id(user_id) direct_conversation_id = Activity.direct_conversation_id(activity, user) diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex index 749a573ba..4f6c9bc9f 100644 --- a/lib/pleroma/web/push/subscription.ex +++ b/lib/pleroma/web/push/subscription.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Push.Subscription do diff --git a/lib/pleroma/web/rel_me.ex b/lib/pleroma/web/rel_me.ex index 28f75b18d..7e745d07e 100644 --- a/lib/pleroma/web/rel_me.ex +++ b/lib/pleroma/web/rel_me.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RelMe do @@ -12,8 +12,9 @@ defmodule Pleroma.Web.RelMe do if Pleroma.Config.get(:env) == :test do def parse(url) when is_binary(url), do: parse_url(url) else + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) def parse(url) when is_binary(url) do - Cachex.fetch!(:rel_me_cache, url, fn _ -> + @cachex.fetch!(:rel_me_cache, url, fn _ -> {:commit, parse_url(url)} end) rescue diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index 442bf9995..566fc8c8a 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -69,7 +69,7 @@ def fetch_data_for_object(object) do def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do with true <- Config.get([:rich_media, :enabled]), - %Object{} = object <- Object.normalize(activity) do + %Object{} = object <- Object.normalize(activity, fetch: false) do fetch_data_for_object(object) else _ -> %{} diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index c70d2fdba..d6b54943b 100644 --- a/lib/pleroma/web/rich_media/parser.ex +++ b/lib/pleroma/web/rich_media/parser.ex @@ -1,10 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parser do require Logger + @cachex Pleroma.Config.get([:cachex, :provider], Cachex) + defp parsers do Pleroma.Config.get([:rich_media, :parsers]) end @@ -24,7 +26,7 @@ def parse(url) do end defp get_cached_or_parse(url) do - case Cachex.fetch(:rich_media_cache, url, fn -> + case @cachex.fetch(:rich_media_cache, url, fn -> case parse_url(url) do {:ok, _} = res -> {:commit, res} @@ -64,7 +66,7 @@ defp set_error_ttl(_url, {:content_type, _}), do: :ok defp set_error_ttl(url, _reason) do ttl = Pleroma.Config.get([:rich_media, :failure_backoff], 60_000) - Cachex.expire(:rich_media_cache, url, ttl) + @cachex.expire(:rich_media_cache, url, ttl) :ok end @@ -106,7 +108,7 @@ def set_ttl_based_on_image(data, url) do {:ok, ttl} when is_number(ttl) -> ttl = ttl * 1000 - case Cachex.expire_at(:rich_media_cache, url, ttl) do + case @cachex.expire_at(:rich_media_cache, url, ttl) do {:ok, true} -> {:ok, ttl} {:ok, false} -> {:error, :no_key} end diff --git a/lib/pleroma/web/rich_media/parser/ttl.ex b/lib/pleroma/web/rich_media/parser/ttl.ex index 8353f0fff..0b7f14fb2 100644 --- a/lib/pleroma/web/rich_media/parser/ttl.ex +++ b/lib/pleroma/web/rich_media/parser/ttl.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parser.TTL do diff --git a/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex b/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex index fc4ef79c0..c7eb267f3 100644 --- a/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex +++ b/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl do diff --git a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex index 3d577e254..31c3d1e33 100644 --- a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex +++ b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do diff --git a/lib/pleroma/web/rich_media/parsers/o_embed.ex b/lib/pleroma/web/rich_media/parsers/o_embed.ex index 1fe6729c3..09eabec56 100644 --- a/lib/pleroma/web/rich_media/parsers/o_embed.ex +++ b/lib/pleroma/web/rich_media/parsers/o_embed.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do diff --git a/lib/pleroma/web/rich_media/parsers/ogp.ex b/lib/pleroma/web/rich_media/parsers/ogp.ex index b3b3b059c..d0edf1c88 100644 --- a/lib/pleroma/web/rich_media/parsers/ogp.ex +++ b/lib/pleroma/web/rich_media/parsers/ogp.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parsers.OGP do diff --git a/lib/pleroma/web/rich_media/parsers/twitter_card.ex b/lib/pleroma/web/rich_media/parsers/twitter_card.ex index 4a04865d2..0adf84159 100644 --- a/lib/pleroma/web/rich_media/parsers/twitter_card.ex +++ b/lib/pleroma/web/rich_media/parsers/twitter_card.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index aefc9f0be..ccf2ef796 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Router do @@ -37,11 +37,13 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug) end - pipeline :expect_authentication do + # Note: expects _user_ authentication (user-unbound app-bound tokens don't qualify) + pipeline :expect_user_authentication do plug(Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug) end - pipeline :expect_public_instance_or_authentication do + # Note: expects public instance or _user_ authentication (user-unbound tokens don't qualify) + pipeline :expect_public_instance_or_user_authentication do plug(Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug) end @@ -56,6 +58,7 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Web.Plugs.UserEnabledPlug) plug(Pleroma.Web.Plugs.SetUserSessionIdPlug) plug(Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug) + plug(Pleroma.Web.Plugs.UserTrackingPlug) end pipeline :base_api do @@ -65,23 +68,30 @@ defmodule Pleroma.Web.Router do plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec) end - pipeline :api do - plug(:expect_public_instance_or_authentication) + pipeline :no_auth_or_privacy_expectations_api do plug(:base_api) plug(:after_auth) plug(Pleroma.Web.Plugs.IdempotencyPlug) end + # Pipeline for app-related endpoints (no user auth checks — app-bound tokens must be supported) + pipeline :app_api do + plug(:no_auth_or_privacy_expectations_api) + end + + pipeline :api do + plug(:expect_public_instance_or_user_authentication) + plug(:no_auth_or_privacy_expectations_api) + end + pipeline :authenticated_api do - plug(:expect_authentication) - plug(:base_api) - plug(:after_auth) + plug(:expect_user_authentication) + plug(:no_auth_or_privacy_expectations_api) plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug) - plug(Pleroma.Web.Plugs.IdempotencyPlug) end pipeline :admin_api do - plug(:expect_authentication) + plug(:expect_user_authentication) plug(:base_api) plug(Pleroma.Web.Plugs.AdminSecretAuthenticationPlug) plug(:after_auth) @@ -130,7 +140,7 @@ defmodule Pleroma.Web.Router do plug(Pleroma.Web.Plugs.MappedSignatureToIdentityPlug) end - scope "/api/pleroma", Pleroma.Web.TwitterAPI do + scope "/api/v1/pleroma", Pleroma.Web.TwitterAPI do pipe_through(:pleroma_api) get("/password_reset/:token", PasswordController, :reset, as: :reset_password) @@ -140,12 +150,12 @@ defmodule Pleroma.Web.Router do get("/healthcheck", UtilController, :healthcheck) end - scope "/api/pleroma", Pleroma.Web do + scope "/api/v1/pleroma", Pleroma.Web do pipe_through(:pleroma_api) post("/uploader_callback/:upload_path", UploaderController, :callback) end - scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do + scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do pipe_through(:admin_api) put("/users/disable_mfa", AdminAPIController, :disable_mfa) @@ -194,7 +204,7 @@ defmodule Pleroma.Web.Router do get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials) patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials) - get("/users", UserController, :list) + get("/users", UserController, :index) get("/users/:nickname", UserController, :show) get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses) get("/users/:nickname/chats", AdminAPIController, :list_user_chats) @@ -249,7 +259,7 @@ defmodule Pleroma.Web.Router do post("/backups", AdminAPIController, :create_backup) end - scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do + scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do scope "/pack" do pipe_through(:admin_api) @@ -319,6 +329,8 @@ defmodule Pleroma.Web.Router do end scope "/oauth", Pleroma.Web.OAuth do + # Note: use /api/v1/accounts/verify_credentials for userinfo of signed-in user + get("/registration_details", OAuthController, :registration_details) post("/mfa/verify", MFAController, :verify, as: :mfa_verify) @@ -356,6 +368,12 @@ defmodule Pleroma.Web.Router do get("/statuses/:id/reactions", EmojiReactionController, :index) end + scope "/api/v0/pleroma", Pleroma.Web.PleromaAPI do + pipe_through(:authenticated_api) + get("/reports", ReportController, :index) + get("/reports/:id", ReportController, :show) + end + scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do scope [] do pipe_through(:authenticated_api) @@ -408,6 +426,13 @@ defmodule Pleroma.Web.Router do get("/federation_status", InstancesController, :show) end + scope "/api/v2/pleroma", Pleroma.Web.PleromaAPI do + scope [] do + pipe_through(:authenticated_api) + get("/chats", ChatController, :index2) + end + end + scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:authenticated_api) @@ -429,10 +454,9 @@ defmodule Pleroma.Web.Router do post("/accounts/:id/mute", AccountController, :mute) post("/accounts/:id/unmute", AccountController, :unmute) - get("/apps/verify_credentials", AppController, :verify_credentials) - get("/conversations", ConversationController, :index) post("/conversations/:id/read", ConversationController, :mark_as_read) + delete("/conversations/:id", ConversationController, :delete) get("/domain_blocks", DomainBlockController, :index) post("/domain_blocks", DomainBlockController, :create) @@ -521,6 +545,13 @@ defmodule Pleroma.Web.Router do put("/settings", MastoFEController, :put_settings) end + scope "/api/v1", Pleroma.Web.MastodonAPI do + pipe_through(:app_api) + + post("/apps", AppController, :create) + get("/apps/verify_credentials", AppController, :verify_credentials) + end + scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:api) @@ -537,8 +568,6 @@ defmodule Pleroma.Web.Router do get("/instance", InstanceController, :show) get("/instance/peers", InstanceController, :peers) - post("/apps", AppController, :create) - get("/statuses", StatusController, :index) get("/statuses/:id", StatusController, :show) get("/statuses/:id/context", StatusController, :context) @@ -675,6 +704,7 @@ defmodule Pleroma.Web.Router do # The following two are S2S as well, see `ActivityPub.fetch_follow_information_for_user/1`: get("/users/:nickname/followers", ActivityPubController, :followers) get("/users/:nickname/following", ActivityPubController, :following) + get("/users/:nickname/collections/featured", ActivityPubController, :pinned) end scope "/", Pleroma.Web.ActivityPub do @@ -786,6 +816,7 @@ defmodule Pleroma.Web.Router do scope "/", Pleroma.Web.Fallback do get("/registration/:token", RedirectController, :registration_page) get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta) + match(:*, "/api/pleroma*path", LegacyPleromaApiRerouterPlug, []) get("/api*path", RedirectController, :api_not_implemented) get("/*path", RedirectController, :redirector_with_preload) diff --git a/lib/pleroma/web/static_fe/static_fe_controller.ex b/lib/pleroma/web/static_fe/static_fe_controller.ex index bdec0897a..fe485d10d 100644 --- a/lib/pleroma/web/static_fe/static_fe_controller.ex +++ b/lib/pleroma/web/static_fe/static_fe_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.StaticFE.StaticFEController do @@ -122,7 +122,7 @@ defp not_found(conn, message) do end defp get_counts(%Activity{} = activity) do - %Object{data: data} = Object.normalize(activity) + %Object{data: data} = Object.normalize(activity, fetch: false) %{ likes: data["like_count"] || 0, diff --git a/lib/pleroma/web/static_fe/static_fe_view.ex b/lib/pleroma/web/static_fe/static_fe_view.ex index b3d1d1ec8..c04715337 100644 --- a/lib/pleroma/web/static_fe/static_fe_view.ex +++ b/lib/pleroma/web/static_fe/static_fe_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.StaticFE.StaticFEView do diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index 7d4a1304a..fc3bbb130 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Streamer do @@ -151,7 +151,7 @@ def filtered_by_user?(%User{} = user, %Activity{} = item, streamed_type) do recipients = MapSet.new(item.recipients) domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks) - with parent <- Object.normalize(item) || item, + with parent <- Object.normalize(item, fetch: false) || item, true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(item.actor not in &1)), true <- item.data["type"] != "Announce" || item.actor not in reblog_muted_ap_ids, true <- diff --git a/lib/pleroma/web/templates/email/digest.html.eex b/lib/pleroma/web/templates/email/digest.html.eex index 860df5f9c..60eceff22 100644 --- a/lib/pleroma/web/templates/email/digest.html.eex +++ b/lib/pleroma/web/templates/email/digest.html.eex @@ -126,7 +126,7 @@
Image diff --git a/lib/pleroma/web/templates/embed/show.html.eex b/lib/pleroma/web/templates/embed/show.html.eex index 05a3f0ee3..092b52b70 100644 --- a/lib/pleroma/web/templates/embed/show.html.eex +++ b/lib/pleroma/web/templates/embed/show.html.eex @@ -6,7 +6,7 @@
<%= raw (@author.name |> Formatter.emojify(@author.emoji)) %> - <%= full_nickname(@author) %> + @<%= full_nickname(@author) %> diff --git a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex index 3fd150c4e..6688830ba 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex @@ -22,7 +22,7 @@ <% end %> - <%= for tag <- @data["tag"] || [] do %> + <%= for tag <- Pleroma.Object.hashtags(@object) do %> <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex index 42960de7d..592b9dcdc 100644 --- a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex +++ b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex @@ -9,7 +9,6 @@ <%= activity_context(@activity) %> - <%= activity_context(@activity) %> <%= if @data["summary"] do %> <%= escape(@data["summary"]) %> @@ -21,7 +20,9 @@ <%= @data["external_url"] %> <% end %> - <%= for tag <- @data["tag"] || [] do %> + <%= activity_context(@activity) %> + + <%= for tag <- Pleroma.Object.hashtags(@object) do %> <% end %> diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex index cf5874a91..c2de28fe4 100644 --- a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex +++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex @@ -41,7 +41,7 @@ <% end %> <% end %> - <%= for tag <- @data["tag"] || [] do %> + <%= for tag <- Pleroma.Object.hashtags(@object) do %> <% end %> diff --git a/lib/pleroma/web/translation_helpers.ex b/lib/pleroma/web/translation_helpers.ex index 7f78ce1b9..0fe31d189 100644 --- a/lib/pleroma/web/translation_helpers.ex +++ b/lib/pleroma/web/translation_helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TranslationHelpers do diff --git a/lib/pleroma/web/twitter_api/controller.ex b/lib/pleroma/web/twitter_api/controller.ex index f42dba442..077bfa70d 100644 --- a/lib/pleroma/web/twitter_api/controller.ex +++ b/lib/pleroma/web/twitter_api/controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.Controller do @@ -30,11 +30,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do def confirm_email(conn, %{"user_id" => uid, "token" => token}) do with %User{} = user <- User.get_cached_by_id(uid), - true <- user.local and user.confirmation_pending and user.confirmation_token == token, - {:ok, _} <- - user - |> User.confirmation_changeset(need_confirmation: false) - |> User.update_and_set_cache() do + true <- user.local and !user.is_confirmed and user.confirmation_token == token, + {:ok, _} <- User.confirm(user) do redirect(conn, to: "/") end end diff --git a/lib/pleroma/web/twitter_api/controllers/password_controller.ex b/lib/pleroma/web/twitter_api/controllers/password_controller.ex index b1a9d810e..bc04a4d49 100644 --- a/lib/pleroma/web/twitter_api/controllers/password_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/password_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.PasswordController do diff --git a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex index 4480a4922..6ca02fbd7 100644 --- a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 9ead0d626..940a645bb 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.UtilController do @@ -150,7 +150,7 @@ def delete_account(%{assigns: %{user: user}} = conn, params) do def disable_account(%{assigns: %{user: user}} = conn, params) do case CommonAPI.Utils.confirm_current_password(user, params["password"]) do {:ok, user} -> - User.deactivate_async(user) + User.set_activation_async(user, false) json(conn, %{status: "success"}) {:error, msg} -> diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 5d7948507..76ca82d20 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.TwitterAPI do @@ -45,7 +45,6 @@ defp create_user(params, opts) do case User.register(changeset) do {:ok, user} -> - maybe_notify_admins(user) {:ok, user} {:error, changeset} -> @@ -58,21 +57,9 @@ defp create_user(params, opts) do end end - defp maybe_notify_admins(%User{} = account) do - if Pleroma.Config.get([:instance, :account_approval_required]) do - User.all_superusers() - |> Enum.filter(fn user -> not is_nil(user.email) end) - |> Enum.each(fn superuser -> - superuser - |> Pleroma.Emails.AdminEmail.new_unapproved_registration(account) - |> Pleroma.Emails.Mailer.deliver_async() - end) - end - end - def password_reset(nickname_or_email) do with true <- is_binary(nickname_or_email), - %User{local: true, email: email, deactivated: false} = user when is_binary(email) <- + %User{local: true, email: email, is_active: true} = user when is_binary(email) <- User.get_by_nickname_or_email(nickname_or_email), {:ok, token_record} <- Pleroma.PasswordResetToken.create_token(user) do user diff --git a/lib/pleroma/web/twitter_api/views/password_view.ex b/lib/pleroma/web/twitter_api/views/password_view.ex index 41462e4af..a9bb95a2c 100644 --- a/lib/pleroma/web/twitter_api/views/password_view.ex +++ b/lib/pleroma/web/twitter_api/views/password_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.PasswordView do diff --git a/lib/pleroma/web/twitter_api/views/remote_follow_view.ex b/lib/pleroma/web/twitter_api/views/remote_follow_view.ex index c05c7821c..ac3f15eec 100644 --- a/lib/pleroma/web/twitter_api/views/remote_follow_view.ex +++ b/lib/pleroma/web/twitter_api/views/remote_follow_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.RemoteFollowView do diff --git a/lib/pleroma/web/twitter_api/views/token_view.ex b/lib/pleroma/web/twitter_api/views/token_view.ex index c36303625..99884e714 100644 --- a/lib/pleroma/web/twitter_api/views/token_view.ex +++ b/lib/pleroma/web/twitter_api/views/token_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.TokenView do diff --git a/lib/pleroma/web/twitter_api/views/util_view.ex b/lib/pleroma/web/twitter_api/views/util_view.ex index 98eea1d18..9b13c09b3 100644 --- a/lib/pleroma/web/twitter_api/views/util_view.ex +++ b/lib/pleroma/web/twitter_api/views/util_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.UtilView do diff --git a/lib/pleroma/web/uploader_controller.ex b/lib/pleroma/web/uploader_controller.ex index 6533f1c0e..0d42c7ec3 100644 --- a/lib/pleroma/web/uploader_controller.ex +++ b/lib/pleroma/web/uploader_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.UploaderController do diff --git a/lib/pleroma/web/views/email_view.ex b/lib/pleroma/web/views/email_view.ex index bcdee6571..f7659b994 100644 --- a/lib/pleroma/web/views/email_view.ex +++ b/lib/pleroma/web/views/email_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.EmailView do diff --git a/lib/pleroma/web/views/embed_view.ex b/lib/pleroma/web/views/embed_view.ex index 5f50bd155..81e196730 100644 --- a/lib/pleroma/web/views/embed_view.ex +++ b/lib/pleroma/web/views/embed_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.EmbedView do @@ -17,6 +17,8 @@ defmodule Pleroma.Web.EmbedView do use Phoenix.HTML + defdelegate full_nickname(user), to: User + @media_types ["image", "audio", "video"] defp fetch_media_type(%{"mediaType" => mediaType}) do @@ -30,11 +32,6 @@ defp open_content? do ) end - defp full_nickname(user) do - %{host: host} = URI.parse(user.ap_id) - "@" <> user.nickname <> "@" <> host - end - defp status_title(%Activity{object: %Object{data: %{"name" => name}}}) when is_binary(name), do: name diff --git a/lib/pleroma/web/views/error_helpers.ex b/lib/pleroma/web/views/error_helpers.ex index df657a343..d282c04b7 100644 --- a/lib/pleroma/web/views/error_helpers.ex +++ b/lib/pleroma/web/views/error_helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ErrorHelpers do diff --git a/lib/pleroma/web/views/error_view.ex b/lib/pleroma/web/views/error_view.ex index e68d55e08..c9715dc4b 100644 --- a/lib/pleroma/web/views/error_view.ex +++ b/lib/pleroma/web/views/error_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ErrorView do diff --git a/lib/pleroma/web/views/layout_view.ex b/lib/pleroma/web/views/layout_view.ex index 3e49c6549..c2da10f04 100644 --- a/lib/pleroma/web/views/layout_view.ex +++ b/lib/pleroma/web/views/layout_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.LayoutView do diff --git a/lib/pleroma/web/views/mailer/subscription_view.ex b/lib/pleroma/web/views/mailer/subscription_view.ex index 4562a9d6c..1dc80987b 100644 --- a/lib/pleroma/web/views/mailer/subscription_view.ex +++ b/lib/pleroma/web/views/mailer/subscription_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Mailer.SubscriptionView do diff --git a/lib/pleroma/web/views/masto_fe_view.ex b/lib/pleroma/web/views/masto_fe_view.ex index b1669d198..b9055cb7f 100644 --- a/lib/pleroma/web/views/masto_fe_view.ex +++ b/lib/pleroma/web/views/masto_fe_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastoFEView do diff --git a/lib/pleroma/web/views/streamer_view.ex b/lib/pleroma/web/views/streamer_view.ex index 4fc14166d..7706035e9 100644 --- a/lib/pleroma/web/views/streamer_view.ex +++ b/lib/pleroma/web/views/streamer_view.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.StreamerView do diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex index 6629f5356..21b10e654 100644 --- a/lib/pleroma/web/web_finger.ex +++ b/lib/pleroma/web/web_finger.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.WebFinger do @@ -58,12 +58,16 @@ defp gather_links(%User{} = user) do ] ++ Publisher.gather_webfinger_links(user) end + defp gather_aliases(%User{} = user) do + [user.ap_id | user.also_known_as] + end + def represent_user(user, "JSON") do {:ok, user} = User.ensure_keys_present(user) %{ "subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", - "aliases" => [user.ap_id], + "aliases" => gather_aliases(user), "links" => gather_links(user) } end @@ -71,6 +75,11 @@ def represent_user(user, "JSON") do def represent_user(user, "XML") do {:ok, user} = User.ensure_keys_present(user) + aliases = + user + |> gather_aliases() + |> Enum.map(&{:Alias, &1}) + links = gather_links(user) |> Enum.map(fn link -> {:Link, link} end) @@ -79,56 +88,62 @@ def represent_user(user, "XML") do :XRD, %{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"}, [ - {:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"}, - {:Alias, user.ap_id} - ] ++ links + {:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"} + ] ++ aliases ++ links } |> XmlBuilder.to_doc() end - defp webfinger_from_xml(doc) do - subject = XML.string_from_xpath("//Subject", doc) + defp webfinger_from_xml(body) do + with {:ok, doc} <- XML.parse_document(body) do + subject = XML.string_from_xpath("//Subject", doc) - subscribe_address = - ~s{//Link[@rel="http://ostatus.org/schema/1.0/subscribe"]/@template} - |> XML.string_from_xpath(doc) + subscribe_address = + ~s{//Link[@rel="http://ostatus.org/schema/1.0/subscribe"]/@template} + |> XML.string_from_xpath(doc) - ap_id = - ~s{//Link[@rel="self" and @type="application/activity+json"]/@href} - |> XML.string_from_xpath(doc) + ap_id = + ~s{//Link[@rel="self" and @type="application/activity+json"]/@href} + |> XML.string_from_xpath(doc) - data = %{ - "subject" => subject, - "subscribe_address" => subscribe_address, - "ap_id" => ap_id - } + data = %{ + "subject" => subject, + "subscribe_address" => subscribe_address, + "ap_id" => ap_id + } - {:ok, data} + {:ok, data} + end end - defp webfinger_from_json(doc) do - data = - Enum.reduce(doc["links"], %{"subject" => doc["subject"]}, fn link, data -> - case {link["type"], link["rel"]} do - {"application/activity+json", "self"} -> - Map.put(data, "ap_id", link["href"]) + defp webfinger_from_json(body) do + with {:ok, doc} <- Jason.decode(body) do + data = + Enum.reduce(doc["links"], %{"subject" => doc["subject"]}, fn link, data -> + case {link["type"], link["rel"]} do + {"application/activity+json", "self"} -> + Map.put(data, "ap_id", link["href"]) - {"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} -> - Map.put(data, "ap_id", link["href"]) + {"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} -> + Map.put(data, "ap_id", link["href"]) - _ -> - Logger.debug("Unhandled type: #{inspect(link["type"])}") - data - end - end) + {nil, "http://ostatus.org/schema/1.0/subscribe"} -> + Map.put(data, "subscribe_address", link["template"]) - {:ok, data} + _ -> + Logger.debug("Unhandled type: #{inspect(link["type"])}") + data + end + end) + + {:ok, data} + end end def get_template_from_xml(body) do xpath = "//Link[@rel='lrdd']/@template" - with doc when doc != :error <- XML.parse_document(body), + with {:ok, doc} <- XML.parse_document(body), template when template != nil <- XML.string_from_xpath(xpath, doc) do {:ok, template} end @@ -181,15 +196,23 @@ def finger(account) do address, [{"accept", "application/xrd+xml,application/jrd+json"}] ), - {:ok, %{status: status, body: body}} when status in 200..299 <- response do - doc = XML.parse_document(body) + {:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <- + response do + case List.keyfind(headers, "content-type", 0) do + {_, content_type} -> + case Plug.Conn.Utils.media_type(content_type) do + {:ok, "application", subtype, _} when subtype in ~w(xrd+xml xml) -> + webfinger_from_xml(body) - if doc != :error do - webfinger_from_xml(doc) - else - with {:ok, doc} <- Jason.decode(body) do - webfinger_from_json(doc) - end + {:ok, "application", subtype, _} when subtype in ~w(jrd+json json) -> + webfinger_from_json(body) + + _ -> + {:error, {:content_type, content_type}} + end + + _ -> + {:error, {:content_type, nil}} end else e -> diff --git a/lib/pleroma/web/web_finger/web_finger_controller.ex b/lib/pleroma/web/web_finger/web_finger_controller.ex index 9f0938fc0..7944c50ad 100644 --- a/lib/pleroma/web/web_finger/web_finger_controller.ex +++ b/lib/pleroma/web/web_finger/web_finger_controller.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.WebFinger.WebFingerController do diff --git a/lib/pleroma/web/xml.ex b/lib/pleroma/web/xml.ex index c69a86a1e..0ab6e9d32 100644 --- a/lib/pleroma/web/xml.ex +++ b/lib/pleroma/web/xml.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.XML do @@ -31,7 +31,7 @@ def parse_document(text) do |> :binary.bin_to_list() |> :xmerl_scan.string(quiet: true) - doc + {:ok, doc} rescue _e -> Logger.debug("Couldn't parse XML: #{inspect(text)}") diff --git a/lib/pleroma/workers/attachments_cleanup_worker.ex b/lib/pleroma/workers/attachments_cleanup_worker.ex index 58226b395..f5090dae7 100644 --- a/lib/pleroma/workers/attachments_cleanup_worker.ex +++ b/lib/pleroma/workers/attachments_cleanup_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.AttachmentsCleanupWorker do @@ -17,12 +17,14 @@ def perform(%Job{ "object" => %{"data" => %{"attachment" => [_ | _] = attachments, "actor" => actor}} } }) do - attachments - |> Enum.flat_map(fn item -> Enum.map(item["url"], & &1["href"]) end) - |> fetch_objects - |> prepare_objects(actor, Enum.map(attachments, & &1["name"])) - |> filter_objects - |> do_clean + if Pleroma.Config.get([:instance, :cleanup_attachments], false) do + attachments + |> Enum.flat_map(fn item -> Enum.map(item["url"], & &1["href"]) end) + |> fetch_objects + |> prepare_objects(actor, Enum.map(attachments, & &1["name"])) + |> filter_objects + |> do_clean + end {:ok, :success} end @@ -32,21 +34,15 @@ def perform(%Job{args: %{"op" => "cleanup_attachments", "object" => _object}}), defp do_clean({object_ids, attachment_urls}) do uploader = Pleroma.Config.get([Pleroma.Upload, :uploader]) - prefix = - case Pleroma.Config.get([Pleroma.Upload, :base_url]) do - nil -> "media" - _ -> "" - end - base_url = String.trim_trailing( - Pleroma.Config.get([Pleroma.Upload, :base_url], Pleroma.Web.base_url()), + Pleroma.Upload.base_url(), "/" ) Enum.each(attachment_urls, fn href -> href - |> String.trim_leading("#{base_url}/#{prefix}") + |> String.trim_leading("#{base_url}") |> uploader.delete_file() end) diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex index 0647c65ae..1e28384cb 100644 --- a/lib/pleroma/workers/background_worker.ex +++ b/lib/pleroma/workers/background_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.BackgroundWorker do @@ -9,9 +9,9 @@ defmodule Pleroma.Workers.BackgroundWorker do @impl Oban.Worker - def perform(%Job{args: %{"op" => "deactivate_user", "user_id" => user_id, "status" => status}}) do + def perform(%Job{args: %{"op" => "user_activation", "user_id" => user_id, "status" => status}}) do user = User.get_cached_by_id(user_id) - User.perform(:deactivate_async, user, status) + User.perform(:set_activation_async, user, status) end def perform(%Job{args: %{"op" => "delete_user", "user_id" => user_id}}) do diff --git a/lib/pleroma/workers/backup_worker.ex b/lib/pleroma/workers/backup_worker.ex index 5b4985983..9b763b04b 100644 --- a/lib/pleroma/workers/backup_worker.ex +++ b/lib/pleroma/workers/backup_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.BackupWorker do diff --git a/lib/pleroma/workers/cron/digest_emails_worker.ex b/lib/pleroma/workers/cron/digest_emails_worker.ex index 0c56f00fb..83dc75d60 100644 --- a/lib/pleroma/workers/cron/digest_emails_worker.ex +++ b/lib/pleroma/workers/cron/digest_emails_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.Cron.DigestEmailsWorker do diff --git a/lib/pleroma/workers/cron/new_users_digest_worker.ex b/lib/pleroma/workers/cron/new_users_digest_worker.ex index 8bbaed83d..9dfd92228 100644 --- a/lib/pleroma/workers/cron/new_users_digest_worker.ex +++ b/lib/pleroma/workers/cron/new_users_digest_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do diff --git a/lib/pleroma/workers/mailer_worker.ex b/lib/pleroma/workers/mailer_worker.ex index 32273cfa5..592230e7a 100644 --- a/lib/pleroma/workers/mailer_worker.ex +++ b/lib/pleroma/workers/mailer_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.MailerWorker do diff --git a/lib/pleroma/workers/mute_expire_worker.ex b/lib/pleroma/workers/mute_expire_worker.ex index 32a12ba85..8da903e76 100644 --- a/lib/pleroma/workers/mute_expire_worker.ex +++ b/lib/pleroma/workers/mute_expire_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.MuteExpireWorker do diff --git a/lib/pleroma/workers/publisher_worker.ex b/lib/pleroma/workers/publisher_worker.ex index e739c3cd0..6209715b3 100644 --- a/lib/pleroma/workers/publisher_worker.ex +++ b/lib/pleroma/workers/publisher_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.PublisherWorker do diff --git a/lib/pleroma/workers/purge_expired_activity.ex b/lib/pleroma/workers/purge_expired_activity.ex index c168890a2..027171c1e 100644 --- a/lib/pleroma/workers/purge_expired_activity.ex +++ b/lib/pleroma/workers/purge_expired_activity.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.PurgeExpiredActivity do @@ -7,7 +7,7 @@ defmodule Pleroma.Workers.PurgeExpiredActivity do Worker which purges expired activity. """ - use Oban.Worker, queue: :activity_expiration, max_attempts: 1 + use Oban.Worker, queue: :activity_expiration, max_attempts: 1, unique: [period: :infinity] import Ecto.Query diff --git a/lib/pleroma/workers/purge_expired_filter.ex b/lib/pleroma/workers/purge_expired_filter.ex new file mode 100644 index 000000000..4740d52e9 --- /dev/null +++ b/lib/pleroma/workers/purge_expired_filter.ex @@ -0,0 +1,43 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.PurgeExpiredFilter do + @moduledoc """ + Worker which purges expired filters + """ + + use Oban.Worker, queue: :filter_expiration, max_attempts: 1, unique: [period: :infinity] + + import Ecto.Query + + alias Oban.Job + alias Pleroma.Repo + + @spec enqueue(%{filter_id: integer(), expires_at: DateTime.t()}) :: + {:ok, Job.t()} | {:error, Ecto.Changeset.t()} + def enqueue(args) do + {scheduled_at, args} = Map.pop(args, :expires_at) + + args + |> new(scheduled_at: scheduled_at) + |> Oban.insert() + end + + @impl true + def perform(%Job{args: %{"filter_id" => id}}) do + Pleroma.Filter + |> Repo.get(id) + |> Repo.delete() + end + + @spec get_expiration(pos_integer()) :: Job.t() | nil + def get_expiration(id) do + from(j in Job, + where: j.state == "scheduled", + where: j.queue == "filter_expiration", + where: fragment("?->'filter_id' = ?", j.args, ^id) + ) + |> Repo.one() + end +end diff --git a/lib/pleroma/workers/purge_expired_token.ex b/lib/pleroma/workers/purge_expired_token.ex index a81e0cd28..cfdf5c6dc 100644 --- a/lib/pleroma/workers/purge_expired_token.ex +++ b/lib/pleroma/workers/purge_expired_token.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.PurgeExpiredToken do diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex index 1b97af1a8..69125dcd0 100644 --- a/lib/pleroma/workers/receiver_worker.ex +++ b/lib/pleroma/workers/receiver_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.ReceiverWorker do diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex index 27e2e3386..ad4d785a1 100644 --- a/lib/pleroma/workers/remote_fetcher_worker.ex +++ b/lib/pleroma/workers/remote_fetcher_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.RemoteFetcherWorker do diff --git a/lib/pleroma/workers/scheduled_activity_worker.ex b/lib/pleroma/workers/scheduled_activity_worker.ex index dd9986fe4..a4ab9928d 100644 --- a/lib/pleroma/workers/scheduled_activity_worker.ex +++ b/lib/pleroma/workers/scheduled_activity_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.ScheduledActivityWorker do @@ -9,38 +9,50 @@ defmodule Pleroma.Workers.ScheduledActivityWorker do use Pleroma.Workers.WorkerHelper, queue: "scheduled_activities" - alias Pleroma.Config + alias Pleroma.Repo alias Pleroma.ScheduledActivity alias Pleroma.User - alias Pleroma.Web.CommonAPI require Logger @impl Oban.Worker def perform(%Job{args: %{"activity_id" => activity_id}}) do - if Config.get([ScheduledActivity, :enabled]) do - case Pleroma.Repo.get(ScheduledActivity, activity_id) do - %ScheduledActivity{} = scheduled_activity -> - post_activity(scheduled_activity) + with %ScheduledActivity{} = scheduled_activity <- find_scheduled_activity(activity_id), + %User{} = user <- find_user(scheduled_activity.user_id) do + params = atomize_keys(scheduled_activity.params) - _ -> - Logger.error("#{__MODULE__} Couldn't find scheduled activity: #{activity_id}") - end + Repo.transaction(fn -> + {:ok, activity} = Pleroma.Web.CommonAPI.post(user, params) + {:ok, _} = ScheduledActivity.delete(scheduled_activity) + activity + end) + else + {:error, :scheduled_activity_not_found} = error -> + Logger.error("#{__MODULE__} Couldn't find scheduled activity: #{activity_id}") + error + + {:error, :user_not_found} = error -> + Logger.error("#{__MODULE__} Couldn't find user for scheduled activity: #{activity_id}") + error end end - defp post_activity(%ScheduledActivity{user_id: user_id, params: params} = scheduled_activity) do - params = Map.new(params, fn {key, value} -> {String.to_existing_atom(key), value} end) - - with {:delete, {:ok, _}} <- {:delete, ScheduledActivity.delete(scheduled_activity)}, - {:user, %User{} = user} <- {:user, User.get_cached_by_id(user_id)}, - {:post, {:ok, _}} <- {:post, CommonAPI.post(user, params)} do - :ok - else - error -> - Logger.error( - "#{__MODULE__} Couldn't create a status from the scheduled activity: #{inspect(error)}" - ) + defp find_scheduled_activity(id) do + with nil <- Repo.get(ScheduledActivity, id) do + {:error, :scheduled_activity_not_found} end end + + defp find_user(id) do + with nil <- User.get_cached_by_id(id) do + {:error, :user_not_found} + end + end + + defp atomize_keys(map) do + Map.new(map, fn + {key, value} when is_map(value) -> {String.to_existing_atom(key), atomize_keys(value)} + {key, value} -> {String.to_existing_atom(key), value} + end) + end end diff --git a/lib/pleroma/workers/transmogrifier_worker.ex b/lib/pleroma/workers/transmogrifier_worker.ex index 15f36375c..b39c1ea62 100644 --- a/lib/pleroma/workers/transmogrifier_worker.ex +++ b/lib/pleroma/workers/transmogrifier_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.TransmogrifierWorker do diff --git a/lib/pleroma/workers/web_pusher_worker.ex b/lib/pleroma/workers/web_pusher_worker.ex index 0cfdc6a6f..8fc2aff26 100644 --- a/lib/pleroma/workers/web_pusher_worker.ex +++ b/lib/pleroma/workers/web_pusher_worker.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.WebPusherWorker do diff --git a/lib/pleroma/workers/worker_helper.ex b/lib/pleroma/workers/worker_helper.ex index 7d1289be2..4befbeb3b 100644 --- a/lib/pleroma/workers/worker_helper.ex +++ b/lib/pleroma/workers/worker_helper.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.WorkerHelper do diff --git a/lib/pleroma/xml_builder.ex b/lib/pleroma/xml_builder.ex index 33b63a71f..922d3f6ee 100644 --- a/lib/pleroma/xml_builder.ex +++ b/lib/pleroma/xml_builder.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.XmlBuilder do diff --git a/mix.exs b/mix.exs index 8ba2d8fbc..b449c82b3 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do def project do [ app: :pleroma, - version: version("2.2.50"), + version: version("2.3.50"), elixir: "~> 1.9", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), @@ -22,7 +22,7 @@ def project do docs: [ source_url_pattern: "https://git.pleroma.social/pleroma/pleroma/blob/develop/%{path}#L%{line}", - logo: "priv/static/static/logo.png", + logo: "priv/static/images/logo.png", extras: ["README.md", "CHANGELOG.md"] ++ Path.wildcard("docs/**/*.md"), groups_for_extras: [ "Installation manuals": Path.wildcard("docs/installation/*.md"), @@ -38,7 +38,7 @@ def project do include_executables_for: [:unix], applications: [ex_syslogger: :load, syslog: :load, eldap: :transient], steps: [:assemble, &put_otp_version/1, ©_files/1, ©_nginx_config/1], - config_providers: [{Pleroma.Config.ReleaseRuntimeProvider, nil}] + config_providers: [{Pleroma.Config.ReleaseRuntimeProvider, []}] ] ] ] @@ -121,11 +121,11 @@ defp deps do {:phoenix_pubsub, "~> 2.0"}, {:phoenix_ecto, "~> 4.0"}, {:ecto_enum, "~> 1.4"}, + {:ecto_explain, "~> 0.1.2"}, {:ecto_sql, "~> 3.4.4"}, {:postgrex, ">= 0.15.5"}, - {:oban, "~> 2.1.0"}, + {:oban, "~> 2.3.4"}, {:gettext, "~> 0.18"}, - {:pbkdf2_elixir, "~> 1.2"}, {:bcrypt_elixir, "~> 2.2"}, {:trailing_format_plug, "~> 0.0.7"}, {:fast_sanitize, "~> 0.2.0"}, @@ -147,8 +147,8 @@ defp deps do {:earmark, "1.4.15"}, {:bbcode_pleroma, "~> 0.2.0"}, {:crypt, - git: "https://github.com/msantos/crypt.git", - ref: "f63a705f92c26955977ee62a313012e309a4d77a"}, + git: "https://git.pleroma.social/pleroma/elixir-libraries/crypt.git", + ref: "cf2aa3f11632e8b0634810a15b3e612c7526f6a3"}, {:cors_plug, "~> 2.0"}, {:web_push_encryption, "~> 0.3"}, {:swoosh, "~> 1.0"}, @@ -158,7 +158,7 @@ defp deps do {:floki, "~> 0.27"}, {:timex, "~> 3.6"}, {:ueberauth, "~> 0.4"}, - {:linkify, "~> 0.4.0"}, + {:linkify, "~> 0.5.0"}, {:http_signatures, "~> 0.1.0"}, {:telemetry, "~> 0.3"}, {:poolboy, "~> 1.5"}, @@ -194,10 +194,9 @@ defp deps do ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"}, {:restarter, path: "./restarter"}, {:majic, - git: "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", branch: "develop"}, - {:open_api_spex, - git: "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git", - ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"}, + git: "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", + ref: "289cda1b6d0d70ccb2ba508a2b0bd24638db2880"}, + {:open_api_spex, "~> 3.10"}, ## dev & test {:ex_doc, "~> 0.22", only: :dev, runtime: false}, @@ -206,8 +205,11 @@ defp deps do {:mock, "~> 0.3.5", only: :test}, # temporary downgrade for excoveralls, hackney until hackney max_connections bug will be fixed {:excoveralls, "0.12.3", only: :test}, - {:hackney, "1.15.2", override: true}, - {:mox, "~> 0.5", only: :test}, + {:hackney, + git: "https://git.pleroma.social/pleroma/elixir-libraries/hackney.git", + ref: "7d7119f0651515d6d7669c78393fd90950a3ec6e", + override: true}, + {:mox, "~> 1.0", only: :test}, {:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test} ] ++ oauth_deps() end @@ -226,7 +228,9 @@ defp aliases do "ecto.reset": ["ecto.drop", "ecto.setup"], test: ["ecto.create --quiet", "ecto.migrate", "test"], docs: ["pleroma.docs", "docs"], - analyze: ["credo --strict --only=warnings,todo,fixme,consistency,readability"] + analyze: ["credo --strict --only=warnings,todo,fixme,consistency,readability"], + copyright: &add_copyright/1, + "copyright.bump": &bump_copyright/1 ] end @@ -329,4 +333,30 @@ defp version(version) do |> Enum.filter(fn string -> string && string != "" end) |> Enum.join() end + + defp add_copyright(_) do + year = NaiveDateTime.utc_now().year + template = ~s[\ +# Pleroma: A lightweight social networking server +# Copyright © 2017-#{year} Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +] |> String.replace("\n", "\\n") + + find = "find lib test priv -type f \\( -name '*.ex' -or -name '*.exs' \\) -exec " + grep = "grep -L '# Copyright © [0-9\-]* Pleroma' {} \\;" + xargs = "xargs -n1 sed -i'' '1s;^;#{template};'" + + :os.cmd(String.to_charlist("#{find}#{grep} | #{xargs}")) + end + + defp bump_copyright(_) do + year = NaiveDateTime.utc_now().year + find = "find lib test priv -type f \\( -name '*.ex' -or -name '*.exs' \\)" + + xargs = + "xargs sed -i'' 's;# Copyright © [0-9\-]* Pleroma.*$;# Copyright © 2017-#{year} Pleroma Authors ;'" + + :os.cmd(String.to_charlist("#{find} | #{xargs}")) + end end diff --git a/mix.lock b/mix.lock index 06542f18d..22c31e0d6 100644 --- a/mix.lock +++ b/mix.lock @@ -11,29 +11,30 @@ "calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"}, "captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]}, "castore": {:hex, :castore, "0.1.7", "1ca19eee705cde48c9e809e37fdd0730510752cc397745e550f6065a56a701e9", [:mix], [], "hexpm", "a2ae2c13d40e9c308387f1aceb14786dca019ebc2a11484fb2a9f797ea0aa0d8"}, - "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"}, + "certifi": {:git, "https://github.com/certifi/erlang-certifi", "e08b12e8993502240c25b78563993776f87ecd2a", [tag: "2.5.1"]}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "comeonin": {:hex, :comeonin, "5.3.1", "7fe612b739c78c9c1a75186ef2d322ce4d25032d119823269d0aa1e2f1e20025", [:mix], [], "hexpm", "d6222483060c17f0977fad1b7401ef0c5863c985a64352755f366aee3799c245"}, "concurrent_limiter": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter.git", "d81be41024569330f296fc472e24198d7499ba78", [ref: "d81be41024569330f296fc472e24198d7499ba78"]}, - "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, + "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, "cors_plug": {:hex, :cors_plug, "2.0.2", "2b46083af45e4bc79632bd951550509395935d3e7973275b2b743bd63cc942ce", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f0d0e13f71c51fd4ef8b2c7e051388e4dfb267522a83a22392c856de7e46465f"}, "cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.0", "69fdb5cf92df6373e15675eb4018cf629f5d8e35e74841bb637d6596cb797bbc", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "42868c229d9a2900a1501c5d0355bfd46e24c862c322b0b4f5a6f14fe0216753"}, "cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"}, "credo": {:hex, :credo, "1.4.1", "16392f1edd2cdb1de9fe4004f5ab0ae612c92e230433968eab00aafd976282fc", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "155f8a2989ad77504de5d8291fa0d41320fdcaa6a1030472e9967f285f8c7692"}, "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.git", "f63a705f92c26955977ee62a313012e309a4d77a", [ref: "f63a705f92c26955977ee62a313012e309a4d77a"]}, + "crypt": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/crypt.git", "cf2aa3f11632e8b0634810a15b3e612c7526f6a3", [ref: "cf2aa3f11632e8b0634810a15b3e612c7526f6a3"]}, "custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"}, - "db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"}, + "db_connection": {:hex, :db_connection, "2.3.1", "4c9f3ed1ef37471cbdd2762d6655be11e38193904d9c5c1c9389f1b891a3088e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "abaab61780dde30301d840417890bd9f74131041afd02174cf4e10635b3a63f5"}, "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "earmark": {:hex, :earmark, "1.4.15", "2c7f924bf495ec1f65bd144b355d0949a05a254d0ec561740308a54946a67888", [:mix], [{:earmark_parser, ">= 1.4.13", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "3b1209b85bc9f3586f370f7c363f6533788fb4e51db23aa79565875e7f9999ee"}, "earmark_parser": {:hex, :earmark_parser, "1.4.13", "0c98163e7d04a15feb62000e1a891489feb29f3d10cb57d4f845c405852bbef8", [:mix], [], "hexpm", "d602c26af3a0af43d2f2645613f65841657ad6efc9f0e361c3b6c06b578214ba"}, "ecto": {:hex, :ecto, "3.4.6", "08f7afad3257d6eb8613309af31037e16c36808dfda5a3cd0cb4e9738db030e4", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6f13a9e2a62e75c2dcfc7207bfc65645ab387af8360db4c89fee8b5a4bf3f70b"}, "ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"}, + "ecto_explain": {:hex, :ecto_explain, "0.1.2", "a9d504cbd4adc809911f796d5ef7ebb17a576a6d32286c3d464c015bd39d5541", [:mix], [], "hexpm", "1d0e7798ae30ecf4ce34e912e5354a0c1c832b7ebceba39298270b9a9f316330"}, "ecto_sql": {:hex, :ecto_sql, "3.4.5", "30161f81b167d561a9a2df4329c10ae05ff36eca7ccc84628f2c8b9fa1e43323", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "31990c6a3579b36a3c0841d34a94c275e727de8b84f58509da5f1b2032c98ac2"}, "eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"}, - "elixir_make": {:hex, :elixir_make, "0.6.1", "8faa29a5597faba999aeeb72bbb9c91694ef8068f0131192fb199f98d32994ef", [:mix], [], "hexpm", "35d33270680f8d839a4003c3e9f43afb595310a592405a00afc12de4c7f55a18"}, + "elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"}, "esshd": {:hex, :esshd, "0.1.1", "d4dd4c46698093a40a56afecce8a46e246eb35463c457c246dacba2e056f31b5", [:mix], [], "hexpm", "d73e341e3009d390aa36387dc8862860bf9f874c94d9fd92ade2926376f49981"}, "eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm", "b14f1dc204321429479c569cfbe8fb287541184ed040956c8862cb7a677b8406"}, "ex2ms": {:hex, :ex2ms, "1.5.0", "19e27f9212be9a96093fed8cdfbef0a2b56c21237196d26760f11dfcfae58e97", [:mix], [], "hexpm"}, @@ -51,40 +52,40 @@ "gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"}, "gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm"}, "gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"}, - "gettext": {:hex, :gettext, "0.18.0", "406d6b9e0e3278162c2ae1de0a60270452c553536772167e2d701f028116f870", [:mix], [], "hexpm", "c3f850be6367ebe1a08616c2158affe4a23231c70391050bf359d5f92f66a571"}, + "gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"}, "gun": {:git, "https://github.com/ninenines/gun.git", "921c47146b2d9567eac7e9a4d2ccc60fffd4f327", [ref: "921c47146b2d9567eac7e9a4d2ccc60fffd4f327"]}, - "hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e0100f8ef7d1124222c11ad362c857d3df7cb5f4204054f9f0f4a728666591fc"}, + "hackney": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/hackney.git", "7d7119f0651515d6d7669c78393fd90950a3ec6e", [ref: "7d7119f0651515d6d7669c78393fd90950a3ec6e"]}, "html_entities": {:hex, :html_entities, "0.5.1", "1c9715058b42c35a2ab65edc5b36d0ea66dd083767bef6e3edb57870ef556549", [:mix], [], "hexpm", "30efab070904eb897ff05cd52fa61c1025d7f8ef3a9ca250bc4e6513d16c32de"}, "html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"}, "http_signatures": {:hex, :http_signatures, "0.1.0", "4e4b501a936dbf4cb5222597038a89ea10781776770d2e185849fa829686b34c", [:mix], [], "hexpm", "f8a7b3731e3fd17d38fa6e343fcad7b03d6874a3b0a108c8568a71ed9c2cf824"}, "httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"}, - "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, + "idna": {:git, "https://github.com/benoitc/erlang-idna", "6cff72747821110169ecfac871b0c69e5064afff", [tag: "6.0.0"]}, "inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"}, "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"}, "joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"}, "jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"}, "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"}, "libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"}, - "linkify": {:hex, :linkify, "0.4.0", "7845b6ac33050a41acaf9318923ce6e7f3854418be9a5f22184de103f7a68ff9", [:mix], [], "hexpm", "a0ceb4c78591fecccf1d99fecc10c13dba75a307c663c80e28af9e2cdd9776ee"}, - "majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", "4c692e544b28d1f5e543fb8a44be090f8cd96f80", [branch: "develop"]}, + "linkify": {:hex, :linkify, "0.5.0", "e0ea8de73ff44742d6a889721221f4c4eccaad5284957ee9832ffeb347602d54", [:mix], [], "hexpm", "4ccd958350aee7c51c89e21f05b15d30596ebbba707e051d21766be1809df2d7"}, + "majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", "289cda1b6d0d70ccb2ba508a2b0bd24638db2880", [ref: "289cda1b6d0d70ccb2ba508a2b0bd24638db2880"]}, "makeup": {:hex, :makeup, "1.0.3", "e339e2f766d12e7260e6672dd4047405963c5ec99661abdc432e6ec67d29ef95", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "2e9b4996d11832947731f7608fed7ad2f9443011b3b479ae288011265cdd3dad"}, "makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"}, "meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, + "metrics": {:git, "https://github.com/benoitc/erlang-metrics", "c6eb4dcf29f9e907539915e2ab996f40c2ec7e8e", [tag: "1.0.1"]}, "mime": {:hex, :mime, "1.4.0", "5066f14944b470286146047d2f73518cf5cca82f8e4815cf35d196b58cf07c47", [:mix], [], "hexpm", "75fa42c4228ea9a23f70f123c74ba7cece6a03b1fd474fe13f6a7a85c6ea4ff6"}, - "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, + "mimerl": {:git, "https://github.com/benoitc/mimerl", "5a1b22a8fada5b3b40438da00a6923cb87a42bbc", [tag: "1.2.0"]}, "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"}, "mock": {:hex, :mock, "0.3.5", "feb81f52b8dcf0a0d65001d2fec459f6b6a8c22562d94a965862f6cc066b5431", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "6fae404799408300f863550392635d8f7e3da6b71abdd5c393faf41b131c8728"}, "mogrify": {:hex, :mogrify, "0.7.4", "9b2496dde44b1ce12676f85d7dc531900939e6367bc537c7243a1b089435b32d", [:mix], [], "hexpm", "50d79e337fba6bc95bfbef918058c90f50b17eed9537771e61d4619488f099c3"}, - "mox": {:hex, :mox, "0.5.2", "55a0a5ba9ccc671518d068c8dddd20eeb436909ea79d1799e2209df7eaa98b6c", [:mix], [], "hexpm", "df4310628cd628ee181df93f50ddfd07be3e5ecc30232d3b6aadf30bdfe6092b"}, + "mox": {:hex, :mox, "1.0.0", "4b3c7005173f47ff30641ba044eb0fe67287743eec9bd9545e37f3002b0a9f8b", [:mix], [], "hexpm", "201b0a20b7abdaaab083e9cf97884950f8a30a1350a1da403b3145e213c6f4df"}, "myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]}, "nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"}, "nimble_pool": {:hex, :nimble_pool, "0.1.0", "ffa9d5be27eee2b00b0c634eb649aa27f97b39186fec3c493716c2a33e784ec6", [:mix], [], "hexpm", "343a1eaa620ddcf3430a83f39f2af499fe2370390d4f785cd475b4df5acaf3f9"}, "nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]}, - "oban": {:hex, :oban, "2.1.0", "034144686f7e76a102b5d67731f098d98a9e4a52b07c25ad580a01f83a7f1cf5", [:mix], [{:ecto_sql, ">= 3.4.3", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c6f067fa3b308ed9e0e6beb2b34277c9c4e48bf95338edabd8f4a757a26e04c2"}, - "open_api_spex": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git", "f296ac0924ba3cf79c7a588c4c252889df4c2edd", [ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"]}, + "oban": {:hex, :oban, "2.3.4", "ec7509b9af2524d55f529cb7aee93d36131ae0bf0f37706f65d2fe707f4d9fd8", [:mix], [{:ecto_sql, ">= 3.4.3", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c70ca0434758fd1805422ea4446af5e910ddc697c0c861549c8f0eb0cfbd2fdf"}, + "open_api_spex": {:hex, :open_api_spex, "3.10.0", "94e9521ad525b3fcf6dc77da7c45f87fdac24756d4de588cb0816b413e7c1844", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "2dbb2bde3d2b821f06936e8dfaf3284331186556291946d84eeba3750ac28765"}, "p1_utils": {:hex, :p1_utils, "1.0.18", "3fe224de5b2e190d730a3c5da9d6e8540c96484cf4b4692921d1e28f0c32b01c", [:rebar3], [], "hexpm", "1fc8773a71a15553b179c986b22fbeead19b28fe486c332d4929700ffeb71f88"}, - "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, + "parse_trans": {:git, "https://github.com/uwiger/parse_trans.git", "76abb347c3c1d00fb0ccf9e4b43e22b3d2288484", [tag: "3.3.0"]}, "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"}, "phoenix": {:hex, :phoenix, "1.5.6", "8298cdb4e0f943242ba8410780a6a69cbbe972fef199b341a36898dd751bdd66", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0dc4d39af1306b6aa5122729b0a95ca779e42c708c6fe7abbb3d336d5379e956"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"}, @@ -97,7 +98,7 @@ "plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "79fd4fcf34d110605c26560cbae8f23c603ec4158c08298bd4360fdea90bb5cf"}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, - "postgrex": {:hex, :postgrex, "0.15.6", "a464c72010a56e3214fe2b99c1a76faab4c2bb0255cabdef30dea763a3569aa2", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "f99268325ac8f66ffd6c4964faab9e70fbf721234ab2ad238c00f9530b8cdd55"}, + "postgrex": {:hex, :postgrex, "0.15.7", "724410acd48abac529d0faa6c2a379fb8ae2088e31247687b16cacc0e0883372", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "88310c010ff047cecd73d5ceca1d99205e4b1ab1b9abfdab7e00f5c9d20ef8f9"}, "pot": {:hex, :pot, "0.11.0", "61bad869a94534739dd4614a25a619bc5c47b9970e9a0ea5bef4628036fc7a16", [:rebar3], [], "hexpm", "57ee6ee6bdeb639661ffafb9acefe3c8f966e45394de6a766813bb9e1be4e54b"}, "prometheus": {:hex, :prometheus, "4.6.0", "20510f381db1ccab818b4cf2fac5fa6ab5cc91bc364a154399901c001465f46f", [:mix, :rebar3], [], "hexpm", "4905fd2992f8038eccd7aa0cd22f40637ed618c0bed1f75c05aacec15b7545de"}, "prometheus_ecto": {:hex, :prometheus_ecto, "1.4.3", "3dd4da1812b8e0dbee81ea58bb3b62ed7588f2eae0c9e97e434c46807ff82311", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "8d66289f77f913b37eda81fd287340c17e61a447549deb28efc254532b2bed82"}, @@ -110,17 +111,17 @@ "recon": {:hex, :recon, "2.5.1", "430ffa60685ac1efdfb1fe4c97b8767c92d0d92e6e7c3e8621559ba77598678a", [:mix, :rebar3], [], "hexpm", "5721c6b6d50122d8f68cccac712caa1231f97894bab779eff5ff0f886cb44648"}, "remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]}, "sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"}, + "ssl_verify_fun": {:git, "https://github.com/deadtrickster/ssl_verify_fun.erl", "c5718226b0b9f3d1a38ef6ca3c3b4c75f53dda92", [tag: "1.1.4"]}, "sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"}, "swoosh": {:hex, :swoosh, "1.0.6", "6765e334c67dacabe721f0d701c7e5a6f06e4595c90df6f91e73ebd54d555833", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "7c50ef78e4acfd1cbd4907dc1fa87b5540675a6be9dc979d04890f49d7ec1830"}, "syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"}, "telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"}, "tesla": {:hex, :tesla, "1.4.0", "1081bef0124b8bdec1c3d330bbe91956648fb008cf0d3950a369cda466a31a87", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "bf1374a5569f5fca8e641363b63f7347d680d91388880979a33bc12a6eb3e0aa"}, - "timex": {:hex, :timex, "3.6.2", "845cdeb6119e2fef10751c0b247b6c59d86d78554c83f78db612e3290f819bc2", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "26030b46199d02a590be61c2394b37ea25a3664c02fafbeca0b24c972025d47a"}, + "timex": {:hex, :timex, "3.7.3", "df8a2ea814749d700d6878ab9eacac9fdb498ecee2f507cb0002ec172bc24d0f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8691c1d86ca3a7bc14a156e2199dc8927be95d1a8f0e3b69e4bb2d6262c53ac6"}, "trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"}, - "tzdata": {:hex, :tzdata, "1.0.4", "a3baa4709ea8dba552dca165af6ae97c624a2d6ac14bd265165eaa8e8af94af6", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "b02637db3df1fd66dd2d3c4f194a81633d0e4b44308d36c1b2fdfd1e4e6f169b"}, + "tzdata": {:hex, :tzdata, "1.0.5", "69f1ee029a49afa04ad77801febaf69385f3d3e3d1e4b56b9469025677b89a28", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "55519aa2a99e5d2095c1e61cc74c9be69688f8ab75c27da724eb8279ff402a5a"}, "ueberauth": {:hex, :ueberauth, "0.6.3", "d42ace28b870e8072cf30e32e385579c57b9cc96ec74fa1f30f30da9c14f3cc0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "afc293d8a1140d6591b53e3eaf415ca92842cb1d32fad3c450c6f045f7f91b60"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, + "unicode_util_compat": {:git, "https://github.com/benoitc/unicode_util_compat.git", "38d7bc105f51159e8ea3279c40121db9db1e652f", [tag: "0.3.1"]}, "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"}, "web_push_encryption": {:hex, :web_push_encryption, "0.3.0", "598b5135e696fd1404dc8d0d7c0fa2c027244a4e5d5e5a98ba267f14fdeaabc8", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "f10bdd1afe527ede694749fb77a2f22f146a51b054c7fa541c9fd920fba7c875"}, "websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []}, diff --git a/priv/gettext/en/LC_MESSAGES/posix_errors.po b/priv/gettext/en/LC_MESSAGES/posix_errors.po new file mode 100644 index 000000000..4d8fbf1d3 --- /dev/null +++ b/priv/gettext/en/LC_MESSAGES/posix_errors.po @@ -0,0 +1,141 @@ +## This file is a PO Template file. +msgid "eperm" +msgstr "Operation not permitted" + +msgid "eacces" +msgstr "Permission denied" + +msgid "eagain" +msgstr "Resource temporarily unavailable" + +msgid "ebadf" +msgstr "Bad file descriptor" + +msgid "ebadmsg" +msgstr "Bad message" + +msgid "ebusy" +msgstr "Device or resource busy" + +msgid "edeadlk" +msgstr "Resource deadlock avoided" + +msgid "edeadlock" +msgstr "Resource deadlock avoided" + +msgid "edquot" +msgstr "Disk quota exceeded" + +msgid "eexist" +msgstr "File exists" + +msgid "efault" +msgstr "Bad address" + +msgid "efbig" +msgstr "File is too large" + +msgid "eftype" +msgstr "Inappropriate file type or format" + +msgid "eintr" +msgstr "Interrupted system call" + +msgid "einval" +msgstr "Invalid argument" + +msgid "eio" +msgstr "Input/output error" + +msgid "eisdir" +msgstr "Illegal operation on a directory" + +msgid "eloop" +msgstr "Too many levels of symbolic links" + +msgid "emfile" +msgstr "Too many open files" + +msgid "emlink" +msgstr "Too many links" + +msgid "emultihop" +msgstr "Multihop attempted" + +msgid "enametoolong" +msgstr "File name is too long" + +msgid "enfile" +msgstr "Too many open files in system" + +msgid "enobufs" +msgstr "No buffer space available" + +msgid "enodev" +msgstr "No such device" + +msgid "enolck" +msgstr "No locks available" + +msgid "enolink" +msgstr "Link has been severed" + +msgid "enoent" +msgstr "No such file or directory" + +msgid "enomem" +msgstr "Cannot allocate memory" + +msgid "enospc" +msgstr "No space left on device" + +msgid "enosr" +msgstr "Out of streams resources" + +msgid "enostr" +msgstr "Device is not a stream" + +msgid "enosys" +msgstr "Function not implemented" + +msgid "enotblk" +msgstr "Block device required" + +msgid "enotdir" +msgstr "Not a directory" + +msgid "enotsup" +msgstr "Operation not supported" + +msgid "enxio" +msgstr "No such device or address" + +msgid "eopnotsupp" +msgstr "Operation not supported" + +msgid "eoverflow" +msgstr "Value too large for defined data type" + +msgid "epipe" +msgstr "Broken pipe" + +msgid "erange" +msgstr "Numerical result out of range" + +msgid "erofs" +msgstr "Read-only file system" + +msgid "espipe" +msgstr "Illegal seek" + +msgid "esrch" +msgstr "No such process" + +msgid "estale" +msgstr "Stale file handle" + +msgid "etxtbsy" +msgstr "Text file busy" + +msgid "exdev" +msgstr "Invalid cross-device link" diff --git a/priv/gettext/it/LC_MESSAGES/errors.po b/priv/gettext/it/LC_MESSAGES/errors.po index cd0cd6c65..6a6ec058e 100644 --- a/priv/gettext/it/LC_MESSAGES/errors.po +++ b/priv/gettext/it/LC_MESSAGES/errors.po @@ -3,8 +3,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-06-19 14:33+0000\n" -"PO-Revision-Date: 2020-07-09 14:40+0000\n" -"Last-Translator: Ben Is \n" +"PO-Revision-Date: 2021-03-13 09:40+0000\n" +"Last-Translator: Ben Is \n" "Language-Team: Italian \n" "Language: it\n" @@ -45,7 +45,7 @@ msgstr "ha una voce invalida" ## From Ecto.Changeset.validate_exclusion/3 msgid "is reserved" -msgstr "è vietato" +msgstr "è riservato" ## From Ecto.Changeset.validate_confirmation/3 msgid "does not match confirmation" @@ -123,7 +123,7 @@ msgstr "Richiesta invalida" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:425 #, elixir-format msgid "Can't delete object" -msgstr "Non puoi eliminare quest'oggetto" +msgstr "Oggetto non eliminabile" #: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:196 #, elixir-format @@ -160,12 +160,12 @@ msgstr "Non puoi pubblicare un messaggio vuoto senza allegati" #: lib/pleroma/web/common_api/utils.ex:504 #, elixir-format msgid "Comment must be up to %{max_size} characters" -msgstr "I commenti posso al massimo consistere di %{max_size} caratteri" +msgstr "I commenti posso al massimo contenere %{max_size} caratteri" #: lib/pleroma/config/config_db.ex:222 #, elixir-format msgid "Config with params %{params} not found" -msgstr "Configurazione con parametri %{max_size} non trovata" +msgstr "Configurazione con parametri %{params} non trovata" #: lib/pleroma/web/common_api/common_api.ex:95 #, elixir-format @@ -200,7 +200,7 @@ msgstr "Non de-intestato" #: lib/pleroma/web/common_api/common_api.ex:126 #, elixir-format msgid "Could not unrepeat" -msgstr "Non de-ripetuto" +msgstr "Non de-condiviso" #: lib/pleroma/web/common_api/common_api.ex:428 #: lib/pleroma/web/common_api/common_api.ex:437 @@ -310,12 +310,12 @@ msgstr "Il messaggio ha superato la lunghezza massima" #: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31 #, elixir-format msgid "This resource requires authentication." -msgstr "Accedi per leggere." +msgstr "Accedi per poter leggere." #: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206 #, elixir-format msgid "Throttled" -msgstr "Strozzato" +msgstr "Limitato" #: lib/pleroma/web/common_api/common_api.ex:266 #, elixir-format @@ -347,17 +347,17 @@ msgstr "Devi aggiungere un indirizzo email valido" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:389 #, elixir-format msgid "can't read inbox of %{nickname} as %{as_nickname}" -msgstr "non puoi leggere i messaggi privati di %{nickname} come %{as_nickname}" +msgstr "non puoi leggere i messaggi di %{nickname} come %{as_nickname}" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:472 #, elixir-format msgid "can't update outbox of %{nickname} as %{as_nickname}" -msgstr "non puoi aggiornare gli inviati di %{nickname} come %{as_nickname}" +msgstr "non puoi inviare da %{nickname} come %{as_nickname}" #: lib/pleroma/web/common_api/common_api.ex:388 #, elixir-format msgid "conversation is already muted" -msgstr "la conversazione è già zittita" +msgstr "la conversazione è già silenziata" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:316 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:491 @@ -419,7 +419,7 @@ msgstr "Errore interno" #: lib/pleroma/web/oauth/fallback_controller.ex:29 #, elixir-format msgid "Invalid Username/Password" -msgstr "Nome utente/parola d'ordine invalidi" +msgstr "Nome utente/password invalidi" #: lib/pleroma/web/twitter_api/twitter_api.ex:118 #, elixir-format @@ -455,7 +455,7 @@ msgstr "Gestore OAuth non supportato: %{provider}." #: lib/pleroma/uploaders/uploader.ex:72 #, elixir-format msgid "Uploader callback timeout" -msgstr "Callback caricatmento scaduta" +msgstr "Callback caricamento scaduta" #: lib/pleroma/web/uploader_controller.ex:23 #, elixir-format @@ -496,7 +496,7 @@ msgstr "Parametro mancante: %{name}" #: lib/pleroma/web/oauth/oauth_controller.ex:322 #, elixir-format msgid "Password reset is required" -msgstr "Necessario reimpostare parola d'ordine" +msgstr "Necessario reimpostare password" #: lib/pleroma/tests/auth_test_controller.ex:9 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/admin_api_controller.ex:6 @@ -540,34 +540,32 @@ msgstr "" #: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:210 #, elixir-format msgid "Unexpected error occurred while adding file to pack." -msgstr "Errore inaspettato durante l'aggiunta del file al pacchetto." +msgstr "Errore inatteso durante l'aggiunta del file al pacchetto." #: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:138 #, elixir-format msgid "Unexpected error occurred while creating pack." -msgstr "Errore inaspettato durante la creazione del pacchetto." +msgstr "Errore inatteso durante la creazione del pacchetto." #: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:278 #, elixir-format msgid "Unexpected error occurred while removing file from pack." -msgstr "Errore inaspettato durante la rimozione del file dal pacchetto." +msgstr "Errore inatteso durante la rimozione del file dal pacchetto." #: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:250 #, elixir-format msgid "Unexpected error occurred while updating file in pack." -msgstr "Errore inaspettato durante l'aggiornamento del file nel pacchetto." +msgstr "Errore inatteso durante l'aggiornamento del file nel pacchetto." #: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:179 #, elixir-format msgid "Unexpected error occurred while updating pack metadata." -msgstr "Errore inaspettato durante l'aggiornamento dei metadati del pacchetto." +msgstr "Errore inatteso durante l'aggiornamento dei metadati del pacchetto." #: lib/pleroma/plugs/user_is_admin_plug.ex:21 #, elixir-format msgid "User is not an admin." -msgstr "" -"L'utente non è un amministratore." -"OAuth." +msgstr "L'utente non è un amministratore." #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61 #, elixir-format diff --git a/priv/gettext/posix_errors.pot b/priv/gettext/posix_errors.pot new file mode 100644 index 000000000..c9f593944 --- /dev/null +++ b/priv/gettext/posix_errors.pot @@ -0,0 +1,149 @@ +## This file is a PO Template file. +## +## `msgid`s here are often extracted from source code. +## Add new translations manually only if they're dynamic +## translations that can't be statically extracted. +## +## Run `mix gettext.extract` to bring this file up to +## date. Leave `msgstr`s empty as changing them here as no +## effect: edit them in PO (`.po`) files instead. +msgid "eperm" +msgstr "" + +msgid "eacces" +msgstr "" + +msgid "eagain" +msgstr "" + +msgid "ebadf" +msgstr "" + +msgid "ebadmsg" +msgstr "" + +msgid "ebusy" +msgstr "" + +msgid "edeadlk" +msgstr "" + +msgid "edeadlock" +msgstr "" + +msgid "edquot" +msgstr "" + +msgid "eexist" +msgstr "" + +msgid "efault" +msgstr "" + +msgid "efbig" +msgstr "" + +msgid "eftype" +msgstr "" + +msgid "eintr" +msgstr "" + +msgid "einval" +msgstr "" + +msgid "eio" +msgstr "" + +msgid "eisdir" +msgstr "" + +msgid "eloop" +msgstr "" + +msgid "emfile" +msgstr "" + +msgid "emlink" +msgstr "" + +msgid "emultihop" +msgstr "" + +msgid "enametoolong" +msgstr "" + +msgid "enfile" +msgstr "" + +msgid "enobufs" +msgstr "" + +msgid "enodev" +msgstr "" + +msgid "enolck" +msgstr "" + +msgid "enolink" +msgstr "" + +msgid "enoent" +msgstr "" + +msgid "enomem" +msgstr "" + +msgid "enospc" +msgstr "" + +msgid "enosr" +msgstr "" + +msgid "enostr" +msgstr "" + +msgid "enosys" +msgstr "" + +msgid "enotblk" +msgstr "" + +msgid "enotdir" +msgstr "" + +msgid "enotsup" +msgstr "" + +msgid "enxio" +msgstr "" + +msgid "eopnotsupp" +msgstr "" + +msgid "eoverflow" +msgstr "" + +msgid "epipe" +msgstr "" + +msgid "erange" +msgstr "" + +msgid "erofs" +msgstr "" + +msgid "espipe" +msgstr "" + +msgid "esrch" +msgstr "" + +msgid "estale" +msgstr "" + +msgid "etxtbsy" +msgstr "" + +msgid "exdev" +msgstr "" diff --git a/priv/gettext/pt_PT/LC_MESSAGES/errors.po b/priv/gettext/pt_PT/LC_MESSAGES/errors.po new file mode 100644 index 000000000..16d8c971a --- /dev/null +++ b/priv/gettext/pt_PT/LC_MESSAGES/errors.po @@ -0,0 +1,594 @@ +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-01-18 17:19+0000\n" +"PO-Revision-Date: 2021-01-18 17:54+0000\n" +"Last-Translator: João Rodrigues \n" +"Language-Team: Portuguese (Portugal) \n" +"Language: pt_PT\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.0.4\n" + +## This file is a PO Template file. +## +## `msgid`s here are often extracted from source code. +## Add new translations manually only if they're dynamic +## translations that can't be statically extracted. +## +## Run `mix gettext.extract` to bring this file up to +## date. Leave `msgstr`s empty as changing them here as no +## effect: edit them in PO (`.po`) files instead. +## From Ecto.Changeset.cast/4 +msgid "can't be blank" +msgstr "não pode estar em branco" + +## From Ecto.Changeset.unique_constraint/3 +msgid "has already been taken" +msgstr "já se encontra em utilização" + +## From Ecto.Changeset.put_change/3 +msgid "is invalid" +msgstr "é inválido" + +## From Ecto.Changeset.validate_format/3 +msgid "has invalid format" +msgstr "tem um formato inválido" + +## From Ecto.Changeset.validate_subset/3 +msgid "has an invalid entry" +msgstr "tem uma entrada inválida" + +## From Ecto.Changeset.validate_exclusion/3 +msgid "is reserved" +msgstr "é reservado" + +## From Ecto.Changeset.validate_confirmation/3 +msgid "does not match confirmation" +msgstr "não corresponde à confirmação" + +## From Ecto.Changeset.no_assoc_constraint/3 +msgid "is still associated with this entry" +msgstr "ainda se encontra associado a esta entrada" + +msgid "are still associated with this entry" +msgstr "ainda está associado a esta entrada" + +## From Ecto.Changeset.validate_length/3 +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "deve conter %{count} caracter" +msgstr[1] "deve conter %{count} caracteres" + +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "deve ter %{count} item" +msgstr[1] "deve ter %{count} items" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "deve ter pelo menos %{count} caracter" +msgstr[1] "deve ter pelo menos %{count} caracteres" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "deve ter pelo menos %{count} item" +msgstr[1] "deve ter pelo menos %{count} items" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "deve ter pelo menos %{count} caracter" +msgstr[1] "deve ter pelo menos %{count} caracteres" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "deve ter pelo menos %{count} item" +msgstr[1] "deve ter pelo menos %{count} items" + +## From Ecto.Changeset.validate_number/3 +msgid "must be less than %{number}" +msgstr "deve ser menor que %{number}" + +msgid "must be greater than %{number}" +msgstr "deve ser maior que %{number}" + +msgid "must be less than or equal to %{number}" +msgstr "deve ser menor ou igual que %{number}" + +msgid "must be greater than or equal to %{number}" +msgstr "deve ser maior ou igual que %{number}" + +msgid "must be equal to %{number}" +msgstr "deve ser igual a %{number}" + +#: lib/pleroma/web/common_api/common_api.ex:505 +#, elixir-format +msgid "Account not found" +msgstr "Conta não encontrada" + +#: lib/pleroma/web/common_api/common_api.ex:339 +#, elixir-format +msgid "Already voted" +msgstr "Já votou" + +#: lib/pleroma/web/oauth/oauth_controller.ex:359 +#, elixir-format +msgid "Bad request" +msgstr "Pedido inválido" + +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:426 +#, elixir-format +msgid "Can't delete object" +msgstr "Não é possível apagar o objeto" + +#: lib/pleroma/web/controller_helper.ex:105 +#: lib/pleroma/web/controller_helper.ex:111 +#, elixir-format +msgid "Can't display this activity" +msgstr "Não é possível exibir esta atividade" + +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:285 +#, elixir-format +msgid "Can't find user" +msgstr "Não foi possível encontrar o utilizador" + +#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:61 +#, elixir-format +msgid "Can't get favorites" +msgstr "Não foi possível obter os favoritos" + +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:438 +#, elixir-format +msgid "Can't like object" +msgstr "Não foi possível gostar do objeto" + +#: lib/pleroma/web/common_api/utils.ex:563 +#, elixir-format +msgid "Cannot post an empty status without attachments" +msgstr "Não é possível publicar um estado vazio e sem ficheiro anexados" + +#: lib/pleroma/web/common_api/utils.ex:511 +#, elixir-format +msgid "Comment must be up to %{max_size} characters" +msgstr "Comentários devem ter até %{max_size} caracteres" + +#: lib/pleroma/config/config_db.ex:191 +#, elixir-format +msgid "Config with params %{params} not found" +msgstr "Configuração com parâmetros %{params} não encontrada" + +#: lib/pleroma/web/common_api/common_api.ex:181 +#: lib/pleroma/web/common_api/common_api.ex:185 +#, elixir-format +msgid "Could not delete" +msgstr "Não foi possível apagar" + +#: lib/pleroma/web/common_api/common_api.ex:231 +#, elixir-format +msgid "Could not favorite" +msgstr "Não foi possível favoritar" + +#: lib/pleroma/web/common_api/common_api.ex:453 +#, elixir-format +msgid "Could not pin" +msgstr "Não foi possível fixar" + +#: lib/pleroma/web/common_api/common_api.ex:278 +#, elixir-format +msgid "Could not unfavorite" +msgstr "Não foi possível retirar favorito" + +#: lib/pleroma/web/common_api/common_api.ex:463 +#, elixir-format +msgid "Could not unpin" +msgstr "Não foi possível desafixar" + +#: lib/pleroma/web/common_api/common_api.ex:216 +#, elixir-format +msgid "Could not unrepeat" +msgstr "Não foi possível deixar de repetir" + +#: lib/pleroma/web/common_api/common_api.ex:512 +#: lib/pleroma/web/common_api/common_api.ex:521 +#, elixir-format +msgid "Could not update state" +msgstr "Não foi possível atualizar estado" + +#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:207 +#, elixir-format +msgid "Error." +msgstr "Erro." + +#: lib/pleroma/web/twitter_api/twitter_api.ex:106 +#, elixir-format +msgid "Invalid CAPTCHA" +msgstr "CAPTCHA Inválido" + +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:116 +#: lib/pleroma/web/oauth/oauth_controller.ex:568 +#, elixir-format +msgid "Invalid credentials" +msgstr "Credenciais inválidas" + +#: lib/pleroma/plugs/ensure_authenticated_plug.ex:38 +#, elixir-format +msgid "Invalid credentials." +msgstr "Credenciais inválidas." + +#: lib/pleroma/web/common_api/common_api.ex:355 +#, elixir-format +msgid "Invalid indices" +msgstr "Índices inválidos" + +#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:29 +#, elixir-format +msgid "Invalid parameters" +msgstr "Parâmetros inválidos" + +#: lib/pleroma/web/common_api/utils.ex:414 +#, elixir-format +msgid "Invalid password." +msgstr "Palavra-passe inválida." + +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:220 +#, elixir-format +msgid "Invalid request" +msgstr "Pedido inválido" + +#: lib/pleroma/web/twitter_api/twitter_api.ex:109 +#, elixir-format +msgid "Kocaptcha service unavailable" +msgstr "Serviço Kocaptcha indisponível" + +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:112 +#, elixir-format +msgid "Missing parameters" +msgstr "Parâmetros em falta" + +#: lib/pleroma/web/common_api/utils.ex:547 +#, elixir-format +msgid "No such conversation" +msgstr "Não existe tal conversação" + +#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:388 +#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:414 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:456 +#, elixir-format +msgid "No such permission_group" +msgstr "Não existe permission_group" + +#: lib/pleroma/plugs/uploaded_media.ex:84 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:486 lib/pleroma/web/admin_api/controllers/fallback_controller.ex:11 +#: lib/pleroma/web/feed/user_controller.ex:71 lib/pleroma/web/ostatus/ostatus_controller.ex:143 +#, elixir-format +msgid "Not found" +msgstr "Não encontrado" + +#: lib/pleroma/web/common_api/common_api.ex:331 +#, elixir-format +msgid "Poll's author can't vote" +msgstr "O autor da sondagem não pode votar" + +#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20 +#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:37 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:49 +#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:50 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:306 +#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71 +#, elixir-format +msgid "Record not found" +msgstr "Registo não encontrado" + +#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:35 +#: lib/pleroma/web/feed/user_controller.ex:77 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:36 +#: lib/pleroma/web/ostatus/ostatus_controller.ex:149 +#, elixir-format +msgid "Something went wrong" +msgstr "Algo ocorreu de errado" + +#: lib/pleroma/web/common_api/activity_draft.ex:107 +#, elixir-format +msgid "The message visibility must be direct" +msgstr "A visibilidade da mensagem deve ser direta" + +#: lib/pleroma/web/common_api/utils.ex:573 +#, elixir-format +msgid "The status is over the character limit" +msgstr "O estado está acima do limite de caracteres" + +#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31 +#, elixir-format +msgid "This resource requires authentication." +msgstr "Este recurso requer autenticação." + +#: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206 +#, elixir-format +msgid "Throttled" +msgstr "Limitado" + +#: lib/pleroma/web/common_api/common_api.ex:356 +#, elixir-format +msgid "Too many choices" +msgstr "Demasiadas opções" + +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:443 +#, elixir-format +msgid "Unhandled activity type" +msgstr "Tipo de atividade não controlada" + +#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:485 +#, elixir-format +msgid "You can't revoke your own admin status." +msgstr "Não podes revogar o teu próprio estatuto de admin." + +#: lib/pleroma/web/oauth/oauth_controller.ex:221 +#: lib/pleroma/web/oauth/oauth_controller.ex:308 +#, elixir-format +msgid "Your account is currently disabled" +msgstr "A tua conta está atualmente desativada" + +#: lib/pleroma/web/oauth/oauth_controller.ex:183 +#: lib/pleroma/web/oauth/oauth_controller.ex:331 +#, elixir-format +msgid "Your login is missing a confirmed e-mail address" +msgstr "" +"O teu início de sessão necessita que tenhas o endereço de e-mail confirmado" + +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:390 +#, elixir-format +msgid "can't read inbox of %{nickname} as %{as_nickname}" +msgstr "" +"não foi possível ler a caixa de entrada de %{nickname} como %{as_nickname}" + +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:473 +#, elixir-format +msgid "can't update outbox of %{nickname} as %{as_nickname}" +msgstr "" +"não foi possível atualizar caixa de saída de %{nickname} como %{as_nickname}" + +#: lib/pleroma/web/common_api/common_api.ex:471 +#, elixir-format +msgid "conversation is already muted" +msgstr "conversação já silenciada" + +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:314 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:492 +#, elixir-format +msgid "error" +msgstr "erro" + +#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:32 +#, elixir-format +msgid "mascots can only be images" +msgstr "mascotes apenas podem ser imagens" + +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:62 +#, elixir-format +msgid "not found" +msgstr "não encontrado" + +#: lib/pleroma/web/oauth/oauth_controller.ex:394 +#, elixir-format +msgid "Bad OAuth request." +msgstr "Pedido OAuth inválido." + +#: lib/pleroma/web/twitter_api/twitter_api.ex:115 +#, elixir-format +msgid "CAPTCHA already used" +msgstr "CPATCHA já utilizado" + +#: lib/pleroma/web/twitter_api/twitter_api.ex:112 +#, elixir-format +msgid "CAPTCHA expired" +msgstr "CAPTCHA expirado" + +#: lib/pleroma/plugs/uploaded_media.ex:57 +#, elixir-format +msgid "Failed" +msgstr "Falhou" + +#: lib/pleroma/web/oauth/oauth_controller.ex:410 +#, elixir-format +msgid "Failed to authenticate: %{message}." +msgstr "Falha ao autenticar: %{message}." + +#: lib/pleroma/web/oauth/oauth_controller.ex:441 +#, elixir-format +msgid "Failed to set up user account." +msgstr "Falha ao configurar conta de utilizador." + +#: lib/pleroma/plugs/oauth_scopes_plug.ex:38 +#, elixir-format +msgid "Insufficient permissions: %{permissions}." +msgstr "Permissões insuficientes: %{permissions}." + +#: lib/pleroma/plugs/uploaded_media.ex:104 +#, elixir-format +msgid "Internal Error" +msgstr "Erro Interno" + +#: lib/pleroma/web/oauth/fallback_controller.ex:22 +#: lib/pleroma/web/oauth/fallback_controller.ex:29 +#, elixir-format +msgid "Invalid Username/Password" +msgstr "Nome de Utilizador/Palavra-passe inválidos" + +#: lib/pleroma/web/twitter_api/twitter_api.ex:118 +#, elixir-format +msgid "Invalid answer data" +msgstr "Informação de resposta inválida" + +#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:33 +#, elixir-format +msgid "Nodeinfo schema version not handled" +msgstr "Versão do schema de nodeinfo não tratado" + +#: lib/pleroma/web/oauth/oauth_controller.ex:172 +#, elixir-format +msgid "This action is outside the authorized scopes" +msgstr "Esta ação está fora dos escopos autorizados" + +#: lib/pleroma/web/oauth/fallback_controller.ex:14 +#, elixir-format +msgid "Unknown error, please check the details and try again." +msgstr "Erro desconhecido, verifica os detalhes e tenta novamente." + +#: lib/pleroma/web/oauth/oauth_controller.ex:119 +#: lib/pleroma/web/oauth/oauth_controller.ex:158 +#, elixir-format +msgid "Unlisted redirect_uri." +msgstr "redirect_uri não listado." + +#: lib/pleroma/web/oauth/oauth_controller.ex:390 +#, elixir-format +msgid "Unsupported OAuth provider: %{provider}." +msgstr "Portal OAuth não suportado: %{provider}." + +#: lib/pleroma/uploaders/uploader.ex:72 +#, elixir-format +msgid "Uploader callback timeout" +msgstr "Tempo expirado para callback de quem envia" + +#: lib/pleroma/web/uploader_controller.ex:23 +#, elixir-format +msgid "bad request" +msgstr "pedido inválido" + +#: lib/pleroma/web/twitter_api/twitter_api.ex:103 +#, elixir-format +msgid "CAPTCHA Error" +msgstr "Erro de CAPTCHA" + +#: lib/pleroma/web/common_api/common_api.ex:290 +#, elixir-format +msgid "Could not add reaction emoji" +msgstr "Não foi possível adicionar reação" + +#: lib/pleroma/web/common_api/common_api.ex:301 +#, elixir-format +msgid "Could not remove reaction emoji" +msgstr "Não foi possível remover reação" + +#: lib/pleroma/web/twitter_api/twitter_api.ex:129 +#, elixir-format +msgid "Invalid CAPTCHA (Missing parameter: %{name})" +msgstr "CAPTCHA inválido (Falta o parâmetro: %{name})" + +#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:92 +#, elixir-format +msgid "List not found" +msgstr "Lista não encontrada" + +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:123 +#, elixir-format +msgid "Missing parameter: %{name}" +msgstr "Parâmetro em falta: %{name}" + +#: lib/pleroma/web/oauth/oauth_controller.ex:210 +#: lib/pleroma/web/oauth/oauth_controller.ex:321 +#, elixir-format +msgid "Password reset is required" +msgstr "É necessário repor palavra-passe" + +#: lib/pleroma/tests/auth_test_controller.ex:9 +#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/config_controller.ex:6 lib/pleroma/web/admin_api/controllers/fallback_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/invite_controller.ex:6 lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/oauth_app_controller.ex:6 lib/pleroma/web/admin_api/controllers/relay_controller.ex:6 +#: lib/pleroma/web/admin_api/controllers/report_controller.ex:6 lib/pleroma/web/admin_api/controllers/status_controller.ex:6 +#: lib/pleroma/web/controller_helper.ex:6 lib/pleroma/web/embed_controller.ex:6 +#: lib/pleroma/web/fallback_redirect_controller.ex:6 lib/pleroma/web/feed/tag_controller.ex:6 +#: lib/pleroma/web/feed/user_controller.ex:6 lib/pleroma/web/mailer/subscription_controller.ex:2 +#: lib/pleroma/web/masto_fe_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/account_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/app_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/auth_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/filter_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/instance_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/list_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/marker_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex:14 +#: lib/pleroma/web/mastodon_api/controllers/media_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/notification_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/report_controller.ex:8 +#: lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/search_controller.ex:6 +#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:7 +#: lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:6 +#: lib/pleroma/web/media_proxy/media_proxy_controller.ex:6 lib/pleroma/web/mongooseim/mongoose_im_controller.ex:6 +#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:6 lib/pleroma/web/oauth/fallback_controller.ex:6 +#: lib/pleroma/web/oauth/mfa_controller.ex:10 lib/pleroma/web/oauth/oauth_controller.ex:6 +#: lib/pleroma/web/ostatus/ostatus_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/account_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/chat_controller.ex:5 lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:2 lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/notification_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex:6 +#: lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex:7 lib/pleroma/web/static_fe/static_fe_controller.ex:6 +#: lib/pleroma/web/twitter_api/controllers/password_controller.ex:10 lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex:6 +#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:6 lib/pleroma/web/twitter_api/twitter_api_controller.ex:6 +#: lib/pleroma/web/uploader_controller.ex:6 lib/pleroma/web/web_finger/web_finger_controller.ex:6 +#, elixir-format +msgid "Security violation: OAuth scopes check was neither handled nor explicitly skipped." +msgstr "" +"Violação de segurança: a verificação de escopo OAuth não foi nem tratada nem " +"explicitamente ignorada." + +#: lib/pleroma/plugs/ensure_authenticated_plug.ex:28 +#, elixir-format +msgid "Two-factor authentication enabled, you must use a access token." +msgstr "" +"Autenticação de dois fatores ativada, deves utilizar uma token de acesso." + +#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:210 +#, elixir-format +msgid "Unexpected error occurred while adding file to pack." +msgstr "Ocorreu um erro inesperado ao adicionar ficheiro ao pack." + +#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:138 +#, elixir-format +msgid "Unexpected error occurred while creating pack." +msgstr "Ocorreu um erro inesperado ao criar o pack." + +#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:278 +#, elixir-format +msgid "Unexpected error occurred while removing file from pack." +msgstr "Ocorreu um erro inesperado ao remover ficheiro do pack." + +#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:250 +#, elixir-format +msgid "Unexpected error occurred while updating file in pack." +msgstr "Ocorreu um erro inesperado a atualizar ficheiro no pack." + +#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:179 +#, elixir-format +msgid "Unexpected error occurred while updating pack metadata." +msgstr "Ocorreu um erro inesperado a atualizar os metadados do pack." + +#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61 +#, elixir-format +msgid "Web push subscription is disabled on this Pleroma instance" +msgstr "" +"Subscrição de notificações push no browser está desativada nesta instância " +"do Pleroma" + +#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:451 +#, elixir-format +msgid "You can't revoke your own admin/moderator status." +msgstr "Não podes revogar o teu próprio estatuto de admin/moderador." + +#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:126 +#, elixir-format +msgid "authorization required for timeline view" +msgstr "autorização necessária para visualizar cronologia" + +#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:24 +#, elixir-format +msgid "Access denied" +msgstr "Acesso negado" + +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:282 +#, elixir-format +msgid "This API requires an authenticated user" +msgstr "Esta API requer um utilizador autenticado" + +#: lib/pleroma/plugs/user_is_admin_plug.ex:21 +#, elixir-format +msgid "User is not an admin." +msgstr "Utilizador não é um admin." diff --git a/priv/gettext/zh_Hans/LC_MESSAGES/errors.po b/priv/gettext/zh_Hans/LC_MESSAGES/errors.po index 8b24d4a86..ecf1dab6b 100644 --- a/priv/gettext/zh_Hans/LC_MESSAGES/errors.po +++ b/priv/gettext/zh_Hans/LC_MESSAGES/errors.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-09-20 13:18+0000\n" -"PO-Revision-Date: 2020-10-22 18:25+0000\n" +"PO-Revision-Date: 2020-12-14 06:00+0000\n" "Last-Translator: shironeko \n" "Language-Team: Chinese (Simplified) \n" @@ -146,9 +146,9 @@ msgid "Cannot post an empty status without attachments" msgstr "无法发送空白且不包含附件的状态" #: lib/pleroma/web/common_api/utils.ex:511 -#, elixir-format +#, elixir-format, fuzzy msgid "Comment must be up to %{max_size} characters" -msgstr "" +msgstr "评论最多可使用 %{max_size} 字符" #: lib/pleroma/config/config_db.ex:191 #, elixir-format @@ -250,21 +250,21 @@ msgstr "没有该对话" #: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:388 #: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:414 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:456 -#, elixir-format +#, elixir-format, fuzzy msgid "No such permission_group" -msgstr "" +msgstr "没有该权限组" #: lib/pleroma/plugs/uploaded_media.ex:84 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:486 lib/pleroma/web/admin_api/controllers/fallback_controller.ex:11 #: lib/pleroma/web/feed/user_controller.ex:71 lib/pleroma/web/ostatus/ostatus_controller.ex:143 #, elixir-format msgid "Not found" -msgstr "" +msgstr "未找到" #: lib/pleroma/web/common_api/common_api.ex:331 #, elixir-format msgid "Poll's author can't vote" -msgstr "" +msgstr "投票的发起者不能投票" #: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20 #: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:37 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:49 @@ -272,39 +272,39 @@ msgstr "" #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71 #, elixir-format msgid "Record not found" -msgstr "" +msgstr "未找到该记录" #: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:35 #: lib/pleroma/web/feed/user_controller.ex:77 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:36 #: lib/pleroma/web/ostatus/ostatus_controller.ex:149 #, elixir-format msgid "Something went wrong" -msgstr "" +msgstr "发生了一些错误" #: lib/pleroma/web/common_api/activity_draft.ex:107 #, elixir-format msgid "The message visibility must be direct" -msgstr "" +msgstr "该消息必须为私信" #: lib/pleroma/web/common_api/utils.ex:573 #, elixir-format msgid "The status is over the character limit" -msgstr "" +msgstr "状态超过了字符数限制" #: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31 #, elixir-format msgid "This resource requires authentication." -msgstr "" +msgstr "该资源需要认证。" #: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206 -#, elixir-format +#, elixir-format, fuzzy msgid "Throttled" -msgstr "" +msgstr "节流了" #: lib/pleroma/web/common_api/common_api.ex:356 #, elixir-format msgid "Too many choices" -msgstr "" +msgstr "太多选项" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:443 #, elixir-format @@ -314,101 +314,101 @@ msgstr "" #: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:485 #, elixir-format msgid "You can't revoke your own admin status." -msgstr "" +msgstr "您不能撤消自己的管理员权限。" #: lib/pleroma/web/oauth/oauth_controller.ex:221 #: lib/pleroma/web/oauth/oauth_controller.ex:308 #, elixir-format msgid "Your account is currently disabled" -msgstr "" +msgstr "您的账户已被禁用" #: lib/pleroma/web/oauth/oauth_controller.ex:183 #: lib/pleroma/web/oauth/oauth_controller.ex:331 #, elixir-format msgid "Your login is missing a confirmed e-mail address" -msgstr "" +msgstr "您的账户缺少已认证的 e-mail 地址" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:390 #, elixir-format msgid "can't read inbox of %{nickname} as %{as_nickname}" -msgstr "" +msgstr "无法以 %{as_nickname} 读取 %{nickname} 的收件箱" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:473 #, elixir-format msgid "can't update outbox of %{nickname} as %{as_nickname}" -msgstr "" +msgstr "无法以 %{as_nickname} 更新 %{nickname} 的出件箱" #: lib/pleroma/web/common_api/common_api.ex:471 #, elixir-format msgid "conversation is already muted" -msgstr "" +msgstr "对话已经被静音" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:314 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:492 #, elixir-format msgid "error" -msgstr "" +msgstr "错误" #: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:32 #, elixir-format msgid "mascots can only be images" -msgstr "" +msgstr "吉祥物只能是图片" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:62 #, elixir-format msgid "not found" -msgstr "" +msgstr "未找到" #: lib/pleroma/web/oauth/oauth_controller.ex:394 #, elixir-format msgid "Bad OAuth request." -msgstr "" +msgstr "错误的 OAuth 请求。" #: lib/pleroma/web/twitter_api/twitter_api.ex:115 #, elixir-format msgid "CAPTCHA already used" -msgstr "" +msgstr "验证码已被使用" #: lib/pleroma/web/twitter_api/twitter_api.ex:112 #, elixir-format msgid "CAPTCHA expired" -msgstr "" +msgstr "验证码已过期" #: lib/pleroma/plugs/uploaded_media.ex:57 #, elixir-format msgid "Failed" -msgstr "" +msgstr "失败" #: lib/pleroma/web/oauth/oauth_controller.ex:410 -#, elixir-format +#, elixir-format, fuzzy msgid "Failed to authenticate: %{message}." -msgstr "" +msgstr "认证失败:%{message}。" #: lib/pleroma/web/oauth/oauth_controller.ex:441 #, elixir-format msgid "Failed to set up user account." -msgstr "" +msgstr "建立用户帐号失败。" #: lib/pleroma/plugs/oauth_scopes_plug.ex:38 #, elixir-format msgid "Insufficient permissions: %{permissions}." -msgstr "" +msgstr "权限不足:%{permissions}。" #: lib/pleroma/plugs/uploaded_media.ex:104 #, elixir-format msgid "Internal Error" -msgstr "" +msgstr "内部错误" #: lib/pleroma/web/oauth/fallback_controller.ex:22 #: lib/pleroma/web/oauth/fallback_controller.ex:29 #, elixir-format msgid "Invalid Username/Password" -msgstr "" +msgstr "无效的用户名/密码" #: lib/pleroma/web/twitter_api/twitter_api.ex:118 -#, elixir-format +#, elixir-format, fuzzy msgid "Invalid answer data" -msgstr "" +msgstr "无效的回答数据" #: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:33 #, elixir-format @@ -418,12 +418,12 @@ msgstr "" #: lib/pleroma/web/oauth/oauth_controller.ex:172 #, elixir-format msgid "This action is outside the authorized scopes" -msgstr "" +msgstr "此操作在许可范围以外" #: lib/pleroma/web/oauth/fallback_controller.ex:14 #, elixir-format msgid "Unknown error, please check the details and try again." -msgstr "" +msgstr "未知错误,请检查并重试。" #: lib/pleroma/web/oauth/oauth_controller.ex:119 #: lib/pleroma/web/oauth/oauth_controller.ex:158 @@ -434,53 +434,53 @@ msgstr "" #: lib/pleroma/web/oauth/oauth_controller.ex:390 #, elixir-format msgid "Unsupported OAuth provider: %{provider}." -msgstr "" +msgstr "不支持的 OAuth 提供者:%{provider}。" #: lib/pleroma/uploaders/uploader.ex:72 -#, elixir-format +#, elixir-format, fuzzy msgid "Uploader callback timeout" -msgstr "" +msgstr "上传回复超时" #: lib/pleroma/web/uploader_controller.ex:23 #, elixir-format msgid "bad request" -msgstr "" +msgstr "错误的请求" #: lib/pleroma/web/twitter_api/twitter_api.ex:103 #, elixir-format msgid "CAPTCHA Error" -msgstr "" +msgstr "验证码错误" #: lib/pleroma/web/common_api/common_api.ex:290 -#, elixir-format +#, elixir-format, fuzzy msgid "Could not add reaction emoji" -msgstr "" +msgstr "无法添加表情反应" #: lib/pleroma/web/common_api/common_api.ex:301 #, elixir-format msgid "Could not remove reaction emoji" -msgstr "" +msgstr "无法移除表情反应" #: lib/pleroma/web/twitter_api/twitter_api.ex:129 #, elixir-format msgid "Invalid CAPTCHA (Missing parameter: %{name})" -msgstr "" +msgstr "无效的验证码(缺少参数:%{name})" #: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:92 #, elixir-format msgid "List not found" -msgstr "" +msgstr "未找到列表" #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:123 #, elixir-format msgid "Missing parameter: %{name}" -msgstr "" +msgstr "缺少参数:%{name}" #: lib/pleroma/web/oauth/oauth_controller.ex:210 #: lib/pleroma/web/oauth/oauth_controller.ex:321 #, elixir-format msgid "Password reset is required" -msgstr "" +msgstr "需要重置密码" #: lib/pleroma/tests/auth_test_controller.ex:9 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6 @@ -520,61 +520,61 @@ msgid "Security violation: OAuth scopes check was neither handled nor explicitly msgstr "" #: lib/pleroma/plugs/ensure_authenticated_plug.ex:28 -#, elixir-format +#, elixir-format, fuzzy msgid "Two-factor authentication enabled, you must use a access token." -msgstr "" +msgstr "已启用两因素验证,您需要使用访问令牌。" #: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:210 #, elixir-format msgid "Unexpected error occurred while adding file to pack." -msgstr "" +msgstr "向表情包添加文件时发生了没有预料到的错误。" #: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:138 #, elixir-format msgid "Unexpected error occurred while creating pack." -msgstr "" +msgstr "创建表情包时发生了没有预料到的错误。" #: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:278 #, elixir-format msgid "Unexpected error occurred while removing file from pack." -msgstr "" +msgstr "从表情包移除文件时发生了没有预料到的错误。" #: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:250 #, elixir-format msgid "Unexpected error occurred while updating file in pack." -msgstr "" +msgstr "更新表情包内的文件时发生了没有预料到的错误。" #: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:179 #, elixir-format msgid "Unexpected error occurred while updating pack metadata." -msgstr "" +msgstr "更新表情包元数据时发生了没有预料到的错误。" #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61 -#, elixir-format +#, elixir-format, fuzzy msgid "Web push subscription is disabled on this Pleroma instance" -msgstr "" +msgstr "此 Pleroma 实例禁用了网页推送订阅" #: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:451 #, elixir-format msgid "You can't revoke your own admin/moderator status." -msgstr "" +msgstr "您不能撤消自己的管理员权限。" #: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:126 #, elixir-format msgid "authorization required for timeline view" -msgstr "" +msgstr "浏览时间线需要认证" #: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:24 #, elixir-format msgid "Access denied" -msgstr "" +msgstr "拒绝访问" #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:282 #, elixir-format msgid "This API requires an authenticated user" -msgstr "" +msgstr "此 API 需要已认证的用户" #: lib/pleroma/plugs/user_is_admin_plug.ex:21 #, elixir-format msgid "User is not an admin." -msgstr "" +msgstr "该用户不是管理员。" diff --git a/priv/repo/migrations/20190408123347_create_conversations.exs b/priv/repo/migrations/20190408123347_create_conversations.exs index 3eaa6136c..aab6cf802 100644 --- a/priv/repo/migrations/20190408123347_create_conversations.exs +++ b/priv/repo/migrations/20190408123347_create_conversations.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Repo.Migrations.CreateConversations do diff --git a/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs b/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs index 43d616705..bfac09f9e 100644 --- a/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs +++ b/priv/repo/migrations/20190711042021_create_safe_jsonb_set.exs @@ -9,7 +9,7 @@ def change do begin result := jsonb_set(target, path, coalesce(new_value, 'null'::jsonb), create_missing); if result is NULL then - raise 'jsonb_set tried to wipe the object, please report this incindent to Pleroma bug tracker. https://git.pleroma.social/pleroma/pleroma/issues/new'; + raise 'jsonb_set tried to wipe the object, please report this incident to Pleroma bug tracker. https://git.pleroma.social/pleroma/pleroma/issues/new'; return target; else return result; diff --git a/priv/repo/migrations/20190730055101_add_oban_jobs_table.exs b/priv/repo/migrations/20190730055101_add_oban_jobs_table.exs index 2f201bd05..5214d59cb 100644 --- a/priv/repo/migrations/20190730055101_add_oban_jobs_table.exs +++ b/priv/repo/migrations/20190730055101_add_oban_jobs_table.exs @@ -1,6 +1,9 @@ defmodule Pleroma.Repo.Migrations.AddObanJobsTable do use Ecto.Migration - defdelegate up, to: Oban.Migrations + def up do + Oban.Migrations.up(version: 2) + end + defdelegate down, to: Oban.Migrations end diff --git a/priv/repo/migrations/20190917100019_update_oban.exs b/priv/repo/migrations/20190917100019_update_oban.exs index 157dc54f9..f673675de 100644 --- a/priv/repo/migrations/20190917100019_update_oban.exs +++ b/priv/repo/migrations/20190917100019_update_oban.exs @@ -6,6 +6,6 @@ def up do end def down do - Oban.Migrations.down(version: 2) + Oban.Migrations.down(version: 3) end end diff --git a/priv/repo/migrations/20200402063221_update_oban_jobs_table.exs b/priv/repo/migrations/20200402063221_update_oban_jobs_table.exs index e7ff04008..ca6856798 100644 --- a/priv/repo/migrations/20200402063221_update_oban_jobs_table.exs +++ b/priv/repo/migrations/20200402063221_update_oban_jobs_table.exs @@ -6,6 +6,6 @@ def up do end def down do - Oban.Migrations.down(version: 7) + Oban.Migrations.down(version: 8) end end diff --git a/priv/repo/migrations/20200602150528_create_chat_message_reference.exs b/priv/repo/migrations/20200602150528_create_chat_message_reference.exs index 6f9148b7c..5e57cddcf 100644 --- a/priv/repo/migrations/20200602150528_create_chat_message_reference.exs +++ b/priv/repo/migrations/20200602150528_create_chat_message_reference.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Repo.Migrations.CreateChatMessageReference do diff --git a/priv/repo/migrations/20200825061316_move_activity_expirations_to_oban.exs b/priv/repo/migrations/20200825061316_move_activity_expirations_to_oban.exs index a703af83f..096ab4ce5 100644 --- a/priv/repo/migrations/20200825061316_move_activity_expirations_to_oban.exs +++ b/priv/repo/migrations/20200825061316_move_activity_expirations_to_oban.exs @@ -6,6 +6,8 @@ defmodule Pleroma.Repo.Migrations.MoveActivityExpirationsToOban do def change do Pleroma.Config.Oban.warn() + Application.ensure_all_started(:oban) + Supervisor.start_link([{Oban, Pleroma.Config.get(Oban)}], strategy: :one_for_one, name: Pleroma.Supervisor diff --git a/priv/repo/migrations/20200907092050_move_tokens_expiration_into_oban.exs b/priv/repo/migrations/20200907092050_move_tokens_expiration_into_oban.exs index 9e49ddacb..725c5ab0b 100644 --- a/priv/repo/migrations/20200907092050_move_tokens_expiration_into_oban.exs +++ b/priv/repo/migrations/20200907092050_move_tokens_expiration_into_oban.exs @@ -6,6 +6,8 @@ defmodule Pleroma.Repo.Migrations.MoveTokensExpirationIntoOban do def change do Pleroma.Config.Oban.warn() + Application.ensure_all_started(:oban) + Supervisor.start_link([{Oban, Pleroma.Config.get(Oban)}], strategy: :one_for_one, name: Pleroma.Supervisor diff --git a/priv/repo/migrations/20201012173004_refactor_deactivated_user_field.exs b/priv/repo/migrations/20201012173004_refactor_deactivated_user_field.exs new file mode 100644 index 000000000..58b75b436 --- /dev/null +++ b/priv/repo/migrations/20201012173004_refactor_deactivated_user_field.exs @@ -0,0 +1,22 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.RefactorDeactivatedUserField do + use Ecto.Migration + + def up do + # Flip the values before we change the meaning of the column + execute("UPDATE users SET deactivated = NOT deactivated;") + execute("ALTER TABLE users RENAME COLUMN deactivated TO is_active;") + execute("ALTER TABLE users ALTER COLUMN is_active SET DEFAULT true;") + execute("ALTER INDEX users_deactivated_index RENAME TO users_is_active_index;") + end + + def down do + execute("UPDATE users SET is_active = NOT is_active;") + execute("ALTER TABLE users RENAME COLUMN is_active TO deactivated;") + execute("ALTER TABLE users ALTER COLUMN deactivated SET DEFAULT false;") + execute("ALTER INDEX users_is_active_index RENAME TO users_deactivated_index;") + end +end diff --git a/priv/repo/migrations/20201013141127_refactor_locked_user_field.exs b/priv/repo/migrations/20201013141127_refactor_locked_user_field.exs index 6cd23dbac..3fb643372 100644 --- a/priv/repo/migrations/20201013141127_refactor_locked_user_field.exs +++ b/priv/repo/migrations/20201013141127_refactor_locked_user_field.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Repo.Migrations.RefactorLockedUserField do diff --git a/priv/repo/migrations/20201013144052_refactor_discoverable_user_field.exs b/priv/repo/migrations/20201013144052_refactor_discoverable_user_field.exs index 3fdc190cc..6d6738e90 100644 --- a/priv/repo/migrations/20201013144052_refactor_discoverable_user_field.exs +++ b/priv/repo/migrations/20201013144052_refactor_discoverable_user_field.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Repo.Migrations.RefactorDiscoverableUserField do diff --git a/priv/repo/migrations/20201013184200_refactor_confirmation_pending_user_field.exs b/priv/repo/migrations/20201013184200_refactor_confirmation_pending_user_field.exs new file mode 100644 index 000000000..d0dc42827 --- /dev/null +++ b/priv/repo/migrations/20201013184200_refactor_confirmation_pending_user_field.exs @@ -0,0 +1,20 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.RefactorConfirmationPendingUserField do + use Ecto.Migration + + def up do + # Flip the values before we change the meaning of the column + execute("UPDATE users SET confirmation_pending = NOT confirmation_pending;") + execute("ALTER TABLE users RENAME COLUMN confirmation_pending TO is_confirmed;") + execute("ALTER TABLE users ALTER COLUMN is_confirmed SET DEFAULT true;") + end + + def down do + execute("UPDATE users SET is_confirmed = NOT is_confirmed;") + execute("ALTER TABLE users RENAME COLUMN is_confirmed TO confirmation_pending;") + execute("ALTER TABLE users ALTER COLUMN confirmation_pending SET DEFAULT false;") + end +end diff --git a/priv/repo/migrations/20201016205220_refactor_approval_pending_user_field.exs b/priv/repo/migrations/20201016205220_refactor_approval_pending_user_field.exs new file mode 100644 index 000000000..944dcf8de --- /dev/null +++ b/priv/repo/migrations/20201016205220_refactor_approval_pending_user_field.exs @@ -0,0 +1,20 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.RefactorApprovalPendingUserField do + use Ecto.Migration + + def up do + # Flip the values before we change the meaning of the column + execute("UPDATE users SET approval_pending = NOT approval_pending;") + execute("ALTER TABLE users RENAME COLUMN approval_pending TO is_approved;") + execute("ALTER TABLE users ALTER COLUMN is_approved SET DEFAULT true;") + end + + def down do + execute("UPDATE users SET is_approved = NOT is_approved;") + execute("ALTER TABLE users RENAME COLUMN is_approved TO approval_pending;") + execute("ALTER TABLE users ALTER COLUMN approval_pending SET DEFAULT false;") + end +end diff --git a/priv/repo/migrations/20201217172858_data_migration_prolong_o_auth_tokens_valid_until.exs b/priv/repo/migrations/20201217172858_data_migration_prolong_o_auth_tokens_valid_until.exs new file mode 100644 index 000000000..560cc7447 --- /dev/null +++ b/priv/repo/migrations/20201217172858_data_migration_prolong_o_auth_tokens_valid_until.exs @@ -0,0 +1,13 @@ +defmodule Pleroma.Repo.Migrations.DataMigrationProlongOAuthTokensValidUntil do + use Ecto.Migration + + def up do + expires_in = Pleroma.Config.get!([:oauth2, :token_expires_in]) + valid_until = NaiveDateTime.add(NaiveDateTime.utc_now(), expires_in, :second) + execute("update oauth_tokens set valid_until = '#{valid_until}'") + end + + def down do + :noop + end +end diff --git a/priv/repo/migrations/20201221202251_create_hashtags.exs b/priv/repo/migrations/20201221202251_create_hashtags.exs new file mode 100644 index 000000000..8d2e9ae66 --- /dev/null +++ b/priv/repo/migrations/20201221202251_create_hashtags.exs @@ -0,0 +1,13 @@ +defmodule Pleroma.Repo.Migrations.CreateHashtags do + use Ecto.Migration + + def change do + create_if_not_exists table(:hashtags) do + add(:name, :citext, null: false) + + timestamps() + end + + create_if_not_exists(unique_index(:hashtags, [:name])) + end +end diff --git a/priv/repo/migrations/20201221202252_remove_data_from_hashtags.exs b/priv/repo/migrations/20201221202252_remove_data_from_hashtags.exs new file mode 100644 index 000000000..0442c3b87 --- /dev/null +++ b/priv/repo/migrations/20201221202252_remove_data_from_hashtags.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.RemoveDataFromHashtags do + use Ecto.Migration + + def up do + alter table(:hashtags) do + remove_if_exists(:data, :map) + end + end + + def down do + alter table(:hashtags) do + add_if_not_exists(:data, :map, default: %{}) + end + end +end diff --git a/priv/repo/migrations/20201221203824_create_hashtags_objects.exs b/priv/repo/migrations/20201221203824_create_hashtags_objects.exs new file mode 100644 index 000000000..581f32b3c --- /dev/null +++ b/priv/repo/migrations/20201221203824_create_hashtags_objects.exs @@ -0,0 +1,13 @@ +defmodule Pleroma.Repo.Migrations.CreateHashtagsObjects do + use Ecto.Migration + + def change do + create_if_not_exists table(:hashtags_objects, primary_key: false) do + add(:hashtag_id, references(:hashtags), null: false, primary_key: true) + add(:object_id, references(:objects), null: false, primary_key: true) + end + + # Note: PK index: "hashtags_objects_pkey" PRIMARY KEY, btree (hashtag_id, object_id) + create_if_not_exists(index(:hashtags_objects, [:object_id])) + end +end diff --git a/priv/repo/migrations/20201231185546_confirm_logged_in_users.exs b/priv/repo/migrations/20201231185546_confirm_logged_in_users.exs new file mode 100644 index 000000000..b9656c17b --- /dev/null +++ b/priv/repo/migrations/20201231185546_confirm_logged_in_users.exs @@ -0,0 +1,22 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.ConfirmLoggedInUsers do + use Ecto.Migration + import Ecto.Query + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.OAuth.Token + + def up do + User + |> where([u], u.is_confirmed == false) + |> join(:inner, [u], t in Token, on: t.user_id == u.id) + |> Repo.update_all(set: [is_confirmed: true]) + end + + def down do + :noop + end +end diff --git a/priv/repo/migrations/20210105195018_create_data_migrations.exs b/priv/repo/migrations/20210105195018_create_data_migrations.exs new file mode 100644 index 000000000..5f2e8d96c --- /dev/null +++ b/priv/repo/migrations/20210105195018_create_data_migrations.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.CreateDataMigrations do + use Ecto.Migration + + def change do + create_if_not_exists table(:data_migrations) do + add(:name, :string, null: false) + add(:state, :integer, default: 1) + add(:feature_lock, :boolean, default: false) + add(:params, :map, default: %{}) + add(:data, :map, default: %{}) + + timestamps() + end + + create_if_not_exists(unique_index(:data_migrations, [:name])) + end +end diff --git a/priv/repo/migrations/20210106183301_data_migration_create_populate_hashtags_table.exs b/priv/repo/migrations/20210106183301_data_migration_create_populate_hashtags_table.exs new file mode 100644 index 000000000..cf3cf26a0 --- /dev/null +++ b/priv/repo/migrations/20210106183301_data_migration_create_populate_hashtags_table.exs @@ -0,0 +1,16 @@ +defmodule Pleroma.Repo.Migrations.DataMigrationCreatePopulateHashtagsTable do + use Ecto.Migration + + def up do + dt = NaiveDateTime.utc_now() + + execute( + "INSERT INTO data_migrations(name, inserted_at, updated_at) " <> + "VALUES ('populate_hashtags_table', '#{dt}', '#{dt}') ON CONFLICT DO NOTHING;" + ) + end + + def down do + execute("DELETE FROM data_migrations WHERE name = 'populate_hashtags_table';") + end +end diff --git a/priv/repo/migrations/20210111172254_create_data_migration_failed_ids.exs b/priv/repo/migrations/20210111172254_create_data_migration_failed_ids.exs new file mode 100644 index 000000000..18afa74ac --- /dev/null +++ b/priv/repo/migrations/20210111172254_create_data_migration_failed_ids.exs @@ -0,0 +1,14 @@ +defmodule Pleroma.Repo.Migrations.CreateDataMigrationFailedIds do + use Ecto.Migration + + def change do + create_if_not_exists table(:data_migration_failed_ids, primary_key: false) do + add(:data_migration_id, references(:data_migrations), null: false, primary_key: true) + add(:record_id, :bigint, null: false, primary_key: true) + end + + create_if_not_exists( + unique_index(:data_migration_failed_ids, [:data_migration_id, :record_id]) + ) + end +end diff --git a/priv/repo/migrations/20210113225652_deprecate_public_endpoint.exs b/priv/repo/migrations/20210113225652_deprecate_public_endpoint.exs new file mode 100644 index 000000000..6f470a459 --- /dev/null +++ b/priv/repo/migrations/20210113225652_deprecate_public_endpoint.exs @@ -0,0 +1,57 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.DeprecatePublicEndpoint do + use Ecto.Migration + + def up do + with %Pleroma.ConfigDB{} = s3_config <- + Pleroma.ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Uploaders.S3}), + %Pleroma.ConfigDB{} = upload_config <- + Pleroma.ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Upload}) do + public_endpoint = s3_config.value[:public_endpoint] + + if !is_nil(public_endpoint) do + upload_value = upload_config.value |> Keyword.merge(base_url: public_endpoint) + + upload_config + |> Ecto.Changeset.change(value: upload_value) + |> Pleroma.Repo.update() + + s3_value = s3_config.value |> Keyword.delete(:public_endpoint) + + s3_config + |> Ecto.Changeset.change(value: s3_value) + |> Pleroma.Repo.update() + end + else + _ -> :ok + end + end + + def down do + with %Pleroma.ConfigDB{} = upload_config <- + Pleroma.ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Upload}), + %Pleroma.ConfigDB{} = s3_config <- + Pleroma.ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Uploaders.S3}) do + base_url = upload_config.value[:base_url] + + if !is_nil(base_url) do + s3_value = s3_config.value |> Keyword.merge(public_endpoint: base_url) + + s3_config + |> Ecto.Changeset.change(value: s3_value) + |> Pleroma.Repo.update() + + upload_value = upload_config.value |> Keyword.delete(:base_url) + + upload_config + |> Ecto.Changeset.change(value: upload_value) + |> Pleroma.Repo.update() + end + else + _ -> :ok + end + end +end diff --git a/priv/repo/migrations/20210115205649_upgrade_oban_jobs_to_v9.exs b/priv/repo/migrations/20210115205649_upgrade_oban_jobs_to_v9.exs new file mode 100644 index 000000000..bfb405579 --- /dev/null +++ b/priv/repo/migrations/20210115205649_upgrade_oban_jobs_to_v9.exs @@ -0,0 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.UpgradeObanJobsToV9 do + use Ecto.Migration + + def up do + Oban.Migrations.up(version: 9) + end + + def down do + Oban.Migrations.down(version: 9) + end +end diff --git a/priv/repo/migrations/20210121080964_add_default_text_search_config.exs b/priv/repo/migrations/20210121080964_add_default_text_search_config.exs new file mode 100644 index 000000000..27f600b70 --- /dev/null +++ b/priv/repo/migrations/20210121080964_add_default_text_search_config.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.AddDefaultTextSearchConfig do + use Ecto.Migration + + def change do + execute("DO $$ + BEGIN + execute 'ALTER DATABASE \"'||current_database()||'\" SET default_text_search_config = ''english'' '; + END + $$;") + end +end diff --git a/priv/repo/migrations/20210122151424_add_last_active_at_to_users.exs b/priv/repo/migrations/20210122151424_add_last_active_at_to_users.exs new file mode 100644 index 000000000..9671e495b --- /dev/null +++ b/priv/repo/migrations/20210122151424_add_last_active_at_to_users.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.AddLastActiveAtToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add(:last_active_at, :naive_datetime) + end + + create_if_not_exists(index(:users, [:last_active_at])) + end +end diff --git a/priv/repo/migrations/20210128092834_remove_duplicates_from_activity_expiration_queue.exs b/priv/repo/migrations/20210128092834_remove_duplicates_from_activity_expiration_queue.exs new file mode 100644 index 000000000..309009205 --- /dev/null +++ b/priv/repo/migrations/20210128092834_remove_duplicates_from_activity_expiration_queue.exs @@ -0,0 +1,29 @@ +defmodule Pleroma.Repo.Migrations.RemoveDuplicatesFromActivityExpirationQueue do + use Ecto.Migration + + import Ecto.Query, only: [from: 2] + + def up do + duplicate_ids = + from(j in Oban.Job, + where: j.queue == "activity_expiration", + where: j.worker == "Pleroma.Workers.PurgeExpiredActivity", + where: j.state == "scheduled", + select: + {fragment("(?)->>'activity_id'", j.args), fragment("array_agg(?)", j.id), count(j.id)}, + group_by: fragment("(?)->>'activity_id'", j.args), + having: count(j.id) > 1 + ) + |> Pleroma.Repo.all() + |> Enum.map(fn {_, ids, _} -> + max_id = Enum.max(ids) + List.delete(ids, max_id) + end) + |> List.flatten() + + from(j in Oban.Job, where: j.id in ^duplicate_ids) + |> Pleroma.Repo.delete_all() + end + + def down, do: :noop +end diff --git a/priv/repo/migrations/20210202110641_add_pinned_objects_to_users.exs b/priv/repo/migrations/20210202110641_add_pinned_objects_to_users.exs new file mode 100644 index 000000000..644527246 --- /dev/null +++ b/priv/repo/migrations/20210202110641_add_pinned_objects_to_users.exs @@ -0,0 +1,9 @@ +defmodule Pleroma.Repo.Migrations.AddPinnedObjectsToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add(:pinned_objects, :map) + end + end +end diff --git a/priv/repo/migrations/20210203141144_add_featured_address_to_users.exs b/priv/repo/migrations/20210203141144_add_featured_address_to_users.exs new file mode 100644 index 000000000..0f6a21611 --- /dev/null +++ b/priv/repo/migrations/20210203141144_add_featured_address_to_users.exs @@ -0,0 +1,23 @@ +defmodule Pleroma.Repo.Migrations.AddFeaturedAddressToUsers do + use Ecto.Migration + + def up do + alter table(:users) do + add(:featured_address, :string) + end + + create(index(:users, [:featured_address])) + + execute(""" + + update users set featured_address = concat(ap_id, '/collections/featured') where local = true and featured_address is null; + + """) + end + + def down do + alter table(:users) do + remove(:featured_address) + end + end +end diff --git a/priv/repo/migrations/20210205145000_move_pinned_activities_into_pinned_objects.exs b/priv/repo/migrations/20210205145000_move_pinned_activities_into_pinned_objects.exs new file mode 100644 index 000000000..9aee545e3 --- /dev/null +++ b/priv/repo/migrations/20210205145000_move_pinned_activities_into_pinned_objects.exs @@ -0,0 +1,28 @@ +defmodule Pleroma.Repo.Migrations.MovePinnedActivitiesIntoPinnedObjects do + use Ecto.Migration + + import Ecto.Query + + alias Pleroma.Repo + alias Pleroma.User + + def up do + from(u in User) + |> select([u], {u.id, fragment("?.pinned_activities", u)}) + |> Repo.stream() + |> Stream.each(fn {user_id, pinned_activities_ids} -> + pinned_activities = Pleroma.Activity.all_by_ids_with_object(pinned_activities_ids) + + pins = + Map.new(pinned_activities, fn %{object: %{data: %{"id" => object_id}}} -> + {object_id, NaiveDateTime.utc_now()} + end) + + from(u in User, where: u.id == ^user_id) + |> Repo.update_all(set: [pinned_objects: pins]) + end) + |> Stream.run() + end + + def down, do: :noop +end diff --git a/priv/repo/migrations/20210206045221_remove_pinned_activities_from_users.exs b/priv/repo/migrations/20210206045221_remove_pinned_activities_from_users.exs new file mode 100644 index 000000000..a3ee93f48 --- /dev/null +++ b/priv/repo/migrations/20210206045221_remove_pinned_activities_from_users.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.RemovePinnedActivitiesFromUsers do + use Ecto.Migration + + def up do + alter table(:users) do + remove(:pinned_activities) + end + end + + def down do + alter table(:users) do + add(:pinned_activities, {:array, :string}, default: []) + end + end +end diff --git a/priv/repo/migrations/20210218223811_add_disclose_client_to_users.exs b/priv/repo/migrations/20210218223811_add_disclose_client_to_users.exs new file mode 100644 index 000000000..37c5776ff --- /dev/null +++ b/priv/repo/migrations/20210218223811_add_disclose_client_to_users.exs @@ -0,0 +1,9 @@ +defmodule Pleroma.Repo.Migrations.AddDiscloseClientToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add(:disclose_client, :boolean, default: true) + end + end +end diff --git a/priv/repo/migrations/20210222183840_remove_hashtags_objects_duplicate_index.exs b/priv/repo/migrations/20210222183840_remove_hashtags_objects_duplicate_index.exs new file mode 100644 index 000000000..6c4a2dfdc --- /dev/null +++ b/priv/repo/migrations/20210222183840_remove_hashtags_objects_duplicate_index.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.RemoveHashtagsObjectsDuplicateIndex do + use Ecto.Migration + + @moduledoc "Removes `hashtags_objects_hashtag_id_object_id_index` index (duplicate of PK index)." + + def up do + drop_if_exists(unique_index(:hashtags_objects, [:hashtag_id, :object_id])) + end + + def down, do: nil +end diff --git a/priv/repo/migrations/20210222184616_change_hashtags_name_to_text.exs b/priv/repo/migrations/20210222184616_change_hashtags_name_to_text.exs new file mode 100644 index 000000000..8940b6ca3 --- /dev/null +++ b/priv/repo/migrations/20210222184616_change_hashtags_name_to_text.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.ChangeHashtagsNameToText do + use Ecto.Migration + + def up do + alter table(:hashtags) do + modify(:name, :text) + end + end + + def down do + alter table(:hashtags) do + modify(:name, :citext) + end + end +end diff --git a/priv/repo/migrations/20210401143153_user_notification_settings_fix.exs b/priv/repo/migrations/20210401143153_user_notification_settings_fix.exs new file mode 100644 index 000000000..cf68f1be6 --- /dev/null +++ b/priv/repo/migrations/20210401143153_user_notification_settings_fix.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.UserNotificationSettingsFix do + use Ecto.Migration + + def up do + execute(~s(UPDATE users + SET + notification_settings = '{"followers": true, "follows": true, "non_follows": true, "non_followers": true}'::jsonb WHERE notification_settings IS NULL +)) + + execute("ALTER TABLE users + ALTER COLUMN notification_settings SET NOT NULL") + end + + def down do + :ok + end +end diff --git a/priv/repo/optional_migrations/rum_indexing/20190510135645_add_fts_index_to_objects_two.exs b/priv/repo/optional_migrations/rum_indexing/20190510135645_add_fts_index_to_objects_two.exs index 757afa129..88476fb57 100644 --- a/priv/repo/optional_migrations/rum_indexing/20190510135645_add_fts_index_to_objects_two.exs +++ b/priv/repo/optional_migrations/rum_indexing/20190510135645_add_fts_index_to_objects_two.exs @@ -3,18 +3,28 @@ defmodule Pleroma.Repo.Migrations.AddFtsIndexToObjectsTwo do def up do execute("create extension if not exists rum") - drop_if_exists index(:objects, ["(to_tsvector('english', data->>'content'))"], using: :gin, name: :objects_fts) + + drop_if_exists( + index(:objects, ["(to_tsvector('english', data->>'content'))"], + using: :gin, + name: :objects_fts + ) + ) + alter table(:objects) do add(:fts_content, :tsvector) end execute("CREATE FUNCTION objects_fts_update() RETURNS trigger AS $$ begin - new.fts_content := to_tsvector('english', new.data->>'content'); + new.fts_content := to_tsvector(new.data->>'content'); return new; end $$ LANGUAGE plpgsql") - execute("create index if not exists objects_fts on objects using RUM (fts_content rum_tsvector_addon_ops, inserted_at) with (attach = 'inserted_at', to = 'fts_content');") + + execute( + "create index if not exists objects_fts on objects using RUM (fts_content rum_tsvector_addon_ops, inserted_at) with (attach = 'inserted_at', to = 'fts_content');" + ) execute("CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE ON objects FOR EACH ROW EXECUTE PROCEDURE objects_fts_update()") @@ -23,12 +33,19 @@ def up do end def down do - execute "drop index if exists objects_fts" - execute "drop trigger if exists tsvectorupdate on objects" - execute "drop function if exists objects_fts_update()" + execute("drop index if exists objects_fts") + execute("drop trigger if exists tsvectorupdate on objects") + execute("drop function if exists objects_fts_update()") + alter table(:objects) do remove(:fts_content, :tsvector) end - create_if_not_exists index(:objects, ["(to_tsvector('english', data->>'content'))"], using: :gin, name: :objects_fts) + + create_if_not_exists( + index(:objects, ["(to_tsvector('english', data->>'content'))"], + using: :gin, + name: :objects_fts + ) + ) end end diff --git a/priv/static/adminfe/chunk-03c5.e6a0e2d0.css b/priv/static/adminfe/chunk-03c5.3368e00c.css similarity index 100% rename from priv/static/adminfe/chunk-03c5.e6a0e2d0.css rename to priv/static/adminfe/chunk-03c5.3368e00c.css diff --git a/priv/static/adminfe/chunk-0492.15b0611f.css b/priv/static/adminfe/chunk-0492.15b0611f.css deleted file mode 100644 index 13537842a..000000000 Binary files a/priv/static/adminfe/chunk-0492.15b0611f.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-0537.cd83e5d6.css b/priv/static/adminfe/chunk-0537.76929cff.css similarity index 100% rename from priv/static/adminfe/chunk-0537.cd83e5d6.css rename to priv/static/adminfe/chunk-0537.76929cff.css diff --git a/priv/static/adminfe/chunk-1e1e.5980e665.css b/priv/static/adminfe/chunk-1e1e.5980e665.css new file mode 100644 index 000000000..1b3a9fcab Binary files /dev/null and b/priv/static/adminfe/chunk-1e1e.5980e665.css differ diff --git a/priv/static/adminfe/chunk-1944.731ba892.css b/priv/static/adminfe/chunk-35b1.949db050.css similarity index 100% rename from priv/static/adminfe/chunk-1944.731ba892.css rename to priv/static/adminfe/chunk-35b1.949db050.css diff --git a/priv/static/adminfe/chunk-4770.20caaae1.css b/priv/static/adminfe/chunk-4770.20caaae1.css new file mode 100644 index 000000000..6f2331666 Binary files /dev/null and b/priv/static/adminfe/chunk-4770.20caaae1.css differ diff --git a/priv/static/adminfe/chunk-04b0.7e25cd78.css b/priv/static/adminfe/chunk-606c.7c5b0a08.css similarity index 100% rename from priv/static/adminfe/chunk-04b0.7e25cd78.css rename to priv/static/adminfe/chunk-606c.7c5b0a08.css diff --git a/priv/static/adminfe/chunk-68ea9.8331e95e.css b/priv/static/adminfe/chunk-68ea9.892994aa.css similarity index 100% rename from priv/static/adminfe/chunk-68ea9.8331e95e.css rename to priv/static/adminfe/chunk-68ea9.892994aa.css diff --git a/priv/static/adminfe/chunk-6e81.559b76f9.css b/priv/static/adminfe/chunk-6e81.687d5046.css similarity index 100% rename from priv/static/adminfe/chunk-6e81.559b76f9.css rename to priv/static/adminfe/chunk-6e81.687d5046.css diff --git a/priv/static/adminfe/chunk-ad1e.1a3c5339.css b/priv/static/adminfe/chunk-7041.c5f6eab7.css similarity index 100% rename from priv/static/adminfe/chunk-ad1e.1a3c5339.css rename to priv/static/adminfe/chunk-7041.c5f6eab7.css diff --git a/priv/static/adminfe/chunk-7968.283bc086.css b/priv/static/adminfe/chunk-7968.283bc086.css deleted file mode 100644 index 5d9863d3a..000000000 Binary files a/priv/static/adminfe/chunk-7968.283bc086.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-7968.613084d0.css b/priv/static/adminfe/chunk-7968.613084d0.css new file mode 100644 index 000000000..5794e0a91 Binary files /dev/null and b/priv/static/adminfe/chunk-7968.613084d0.css differ diff --git a/priv/static/adminfe/chunk-8fbb.dd321643.css b/priv/static/adminfe/chunk-8fbb.dd321643.css deleted file mode 100644 index f50d974bd..000000000 Binary files a/priv/static/adminfe/chunk-8fbb.dd321643.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-f625.bcd0ea3b.css b/priv/static/adminfe/chunk-bc60.4417dd06.css similarity index 53% rename from priv/static/adminfe/chunk-f625.bcd0ea3b.css rename to priv/static/adminfe/chunk-bc60.4417dd06.css index ac26ef0f5..59ca45d6c 100644 Binary files a/priv/static/adminfe/chunk-f625.bcd0ea3b.css and b/priv/static/adminfe/chunk-bc60.4417dd06.css differ diff --git a/priv/static/adminfe/chunk-e660.9e75af5b.css b/priv/static/adminfe/chunk-e660.62c077ac.css similarity index 100% rename from priv/static/adminfe/chunk-e660.9e75af5b.css rename to priv/static/adminfe/chunk-e660.62c077ac.css diff --git a/priv/static/adminfe/chunk-f364.4fd16c53.css b/priv/static/adminfe/chunk-f364.4fd16c53.css new file mode 100644 index 000000000..abea7d536 Binary files /dev/null and b/priv/static/adminfe/chunk-f364.4fd16c53.css differ diff --git a/priv/static/adminfe/chunk-f364.6b5f3f0d.css b/priv/static/adminfe/chunk-f364.6b5f3f0d.css deleted file mode 100644 index ec665da84..000000000 Binary files a/priv/static/adminfe/chunk-f364.6b5f3f0d.css and /dev/null differ diff --git a/priv/static/adminfe/index.html b/priv/static/adminfe/index.html index e6af40e97..09915e8cd 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.1428845f.js b/priv/static/adminfe/static/js/app.1428845f.js new file mode 100644 index 000000000..cc9541168 Binary files /dev/null and b/priv/static/adminfe/static/js/app.1428845f.js differ diff --git a/priv/static/adminfe/static/js/app.1428845f.js.map b/priv/static/adminfe/static/js/app.1428845f.js.map new file mode 100644 index 000000000..3fba88015 Binary files /dev/null and b/priv/static/adminfe/static/js/app.1428845f.js.map differ diff --git a/priv/static/adminfe/static/js/app.c67f9a2f.js b/priv/static/adminfe/static/js/app.c67f9a2f.js deleted file mode 100644 index 65f9d4a29..000000000 Binary files a/priv/static/adminfe/static/js/app.c67f9a2f.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/app.c67f9a2f.js.map b/priv/static/adminfe/static/js/app.c67f9a2f.js.map deleted file mode 100644 index 41b4375aa..000000000 Binary files a/priv/static/adminfe/static/js/app.c67f9a2f.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-03c5.6de0c4c7.js b/priv/static/adminfe/static/js/chunk-03c5.1b0ab243.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-03c5.6de0c4c7.js rename to priv/static/adminfe/static/js/chunk-03c5.1b0ab243.js index a89c65572..94dfce1a8 100644 Binary files a/priv/static/adminfe/static/js/chunk-03c5.6de0c4c7.js and b/priv/static/adminfe/static/js/chunk-03c5.1b0ab243.js differ diff --git a/priv/static/adminfe/static/js/chunk-03c5.6de0c4c7.js.map b/priv/static/adminfe/static/js/chunk-03c5.1b0ab243.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-03c5.6de0c4c7.js.map rename to priv/static/adminfe/static/js/chunk-03c5.1b0ab243.js.map index 963ff6dee..acf1ba219 100644 Binary files a/priv/static/adminfe/static/js/chunk-03c5.6de0c4c7.js.map and b/priv/static/adminfe/static/js/chunk-03c5.1b0ab243.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-0492.47abe1dc.js b/priv/static/adminfe/static/js/chunk-0492.47abe1dc.js deleted file mode 100644 index 243ecde70..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0492.47abe1dc.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0492.47abe1dc.js.map b/priv/static/adminfe/static/js/chunk-0492.47abe1dc.js.map deleted file mode 100644 index f5e0d9ebc..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0492.47abe1dc.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-04b0.90c6d24c.js b/priv/static/adminfe/static/js/chunk-04b0.90c6d24c.js deleted file mode 100644 index 9d0352814..000000000 Binary files a/priv/static/adminfe/static/js/chunk-04b0.90c6d24c.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-04b0.90c6d24c.js.map b/priv/static/adminfe/static/js/chunk-04b0.90c6d24c.js.map deleted file mode 100644 index a9bee3721..000000000 Binary files a/priv/static/adminfe/static/js/chunk-04b0.90c6d24c.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0537.74db16b0.js b/priv/static/adminfe/static/js/chunk-0537.74db16b0.js deleted file mode 100644 index 35231e562..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0537.74db16b0.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0537.74db16b0.js.map b/priv/static/adminfe/static/js/chunk-0537.74db16b0.js.map deleted file mode 100644 index fa87bd76d..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0537.74db16b0.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0537.d0eef370.js b/priv/static/adminfe/static/js/chunk-0537.d0eef370.js new file mode 100644 index 000000000..f1b73a18a Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0537.d0eef370.js differ diff --git a/priv/static/adminfe/static/js/chunk-0537.d0eef370.js.map b/priv/static/adminfe/static/js/chunk-0537.d0eef370.js.map new file mode 100644 index 000000000..e0a2f4d21 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0537.d0eef370.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-1944.7bed0c4b.js b/priv/static/adminfe/static/js/chunk-1944.7bed0c4b.js deleted file mode 100644 index 87590c6ce..000000000 Binary files a/priv/static/adminfe/static/js/chunk-1944.7bed0c4b.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-1944.7bed0c4b.js.map b/priv/static/adminfe/static/js/chunk-1944.7bed0c4b.js.map deleted file mode 100644 index 23229293e..000000000 Binary files a/priv/static/adminfe/static/js/chunk-1944.7bed0c4b.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-1e1e.37f6f555.js b/priv/static/adminfe/static/js/chunk-1e1e.37f6f555.js new file mode 100644 index 000000000..65d0aa926 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-1e1e.37f6f555.js differ diff --git a/priv/static/adminfe/static/js/chunk-1e1e.37f6f555.js.map b/priv/static/adminfe/static/js/chunk-1e1e.37f6f555.js.map new file mode 100644 index 000000000..a0b5ca3be Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-1e1e.37f6f555.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-35b1.50c1449b.js b/priv/static/adminfe/static/js/chunk-35b1.50c1449b.js new file mode 100644 index 000000000..75a2bf0c2 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-35b1.50c1449b.js differ diff --git a/priv/static/adminfe/static/js/chunk-35b1.50c1449b.js.map b/priv/static/adminfe/static/js/chunk-35b1.50c1449b.js.map new file mode 100644 index 000000000..b5e2a27b2 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-35b1.50c1449b.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-4770.1c1fff97.js b/priv/static/adminfe/static/js/chunk-4770.1c1fff97.js new file mode 100644 index 000000000..706ede69e Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-4770.1c1fff97.js differ diff --git a/priv/static/adminfe/static/js/chunk-4770.1c1fff97.js.map b/priv/static/adminfe/static/js/chunk-4770.1c1fff97.js.map new file mode 100644 index 000000000..f1303900d Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-4770.1c1fff97.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-606c.8ac52179.js b/priv/static/adminfe/static/js/chunk-606c.8ac52179.js new file mode 100644 index 000000000..7ae3ce7b1 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-606c.8ac52179.js differ diff --git a/priv/static/adminfe/static/js/chunk-606c.8ac52179.js.map b/priv/static/adminfe/static/js/chunk-606c.8ac52179.js.map new file mode 100644 index 000000000..8c41c2755 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-606c.8ac52179.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-68ea9.2b2877d5.js b/priv/static/adminfe/static/js/chunk-68ea9.5a11341a.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-68ea9.2b2877d5.js rename to priv/static/adminfe/static/js/chunk-68ea9.5a11341a.js index 60056454d..b11c19485 100644 Binary files a/priv/static/adminfe/static/js/chunk-68ea9.2b2877d5.js and b/priv/static/adminfe/static/js/chunk-68ea9.5a11341a.js differ diff --git a/priv/static/adminfe/static/js/chunk-68ea9.2b2877d5.js.map b/priv/static/adminfe/static/js/chunk-68ea9.5a11341a.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-68ea9.2b2877d5.js.map rename to priv/static/adminfe/static/js/chunk-68ea9.5a11341a.js.map index 9e26519c3..8779a5e95 100644 Binary files a/priv/static/adminfe/static/js/chunk-68ea9.2b2877d5.js.map and b/priv/static/adminfe/static/js/chunk-68ea9.5a11341a.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-6e81.afade883.js b/priv/static/adminfe/static/js/chunk-6e81.6c4f2ce1.js similarity index 97% rename from priv/static/adminfe/static/js/chunk-6e81.afade883.js rename to priv/static/adminfe/static/js/chunk-6e81.6c4f2ce1.js index 3b5dd6c5c..6fd67c44f 100644 Binary files a/priv/static/adminfe/static/js/chunk-6e81.afade883.js and b/priv/static/adminfe/static/js/chunk-6e81.6c4f2ce1.js differ diff --git a/priv/static/adminfe/static/js/chunk-6e81.afade883.js.map b/priv/static/adminfe/static/js/chunk-6e81.6c4f2ce1.js.map similarity index 98% rename from priv/static/adminfe/static/js/chunk-6e81.afade883.js.map rename to priv/static/adminfe/static/js/chunk-6e81.6c4f2ce1.js.map index a0f7fca19..931f7521e 100644 Binary files a/priv/static/adminfe/static/js/chunk-6e81.afade883.js.map and b/priv/static/adminfe/static/js/chunk-6e81.6c4f2ce1.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-ad1e.eba9db26.js b/priv/static/adminfe/static/js/chunk-7041.390b2ec4.js similarity index 64% rename from priv/static/adminfe/static/js/chunk-ad1e.eba9db26.js rename to priv/static/adminfe/static/js/chunk-7041.390b2ec4.js index 82ddd4df2..50eb1a5f5 100644 Binary files a/priv/static/adminfe/static/js/chunk-ad1e.eba9db26.js and b/priv/static/adminfe/static/js/chunk-7041.390b2ec4.js differ diff --git a/priv/static/adminfe/static/js/chunk-7041.390b2ec4.js.map b/priv/static/adminfe/static/js/chunk-7041.390b2ec4.js.map new file mode 100644 index 000000000..401bb0b1f Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7041.390b2ec4.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-7968.88218960.js b/priv/static/adminfe/static/js/chunk-7968.88218960.js new file mode 100644 index 000000000..44348d9d1 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7968.88218960.js differ diff --git a/priv/static/adminfe/static/js/chunk-7968.88218960.js.map b/priv/static/adminfe/static/js/chunk-7968.88218960.js.map new file mode 100644 index 000000000..6fa0131fc Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7968.88218960.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-7968.f51e3292.js b/priv/static/adminfe/static/js/chunk-7968.f51e3292.js deleted file mode 100644 index dc981706f..000000000 Binary files a/priv/static/adminfe/static/js/chunk-7968.f51e3292.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-7968.f51e3292.js.map b/priv/static/adminfe/static/js/chunk-7968.f51e3292.js.map deleted file mode 100644 index c2f0726b7..000000000 Binary files a/priv/static/adminfe/static/js/chunk-7968.f51e3292.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-8fbb.c847ce9d.js b/priv/static/adminfe/static/js/chunk-8fbb.c847ce9d.js deleted file mode 100644 index 74ffe9194..000000000 Binary files a/priv/static/adminfe/static/js/chunk-8fbb.c847ce9d.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-8fbb.c847ce9d.js.map b/priv/static/adminfe/static/js/chunk-8fbb.c847ce9d.js.map deleted file mode 100644 index b3c3b5fe8..000000000 Binary files a/priv/static/adminfe/static/js/chunk-8fbb.c847ce9d.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-ad1e.eba9db26.js.map b/priv/static/adminfe/static/js/chunk-ad1e.eba9db26.js.map deleted file mode 100644 index d74c2498f..000000000 Binary files a/priv/static/adminfe/static/js/chunk-ad1e.eba9db26.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-bc60.79f8c7e7.js b/priv/static/adminfe/static/js/chunk-bc60.79f8c7e7.js new file mode 100644 index 000000000..b2206aa11 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-bc60.79f8c7e7.js differ diff --git a/priv/static/adminfe/static/js/chunk-bc60.79f8c7e7.js.map b/priv/static/adminfe/static/js/chunk-bc60.79f8c7e7.js.map new file mode 100644 index 000000000..799352270 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-bc60.79f8c7e7.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-e660.feca27c4.js b/priv/static/adminfe/static/js/chunk-e660.2101cafc.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-e660.feca27c4.js rename to priv/static/adminfe/static/js/chunk-e660.2101cafc.js index 5659d263e..20ecbb5a4 100644 Binary files a/priv/static/adminfe/static/js/chunk-e660.feca27c4.js and b/priv/static/adminfe/static/js/chunk-e660.2101cafc.js differ diff --git a/priv/static/adminfe/static/js/chunk-e660.feca27c4.js.map b/priv/static/adminfe/static/js/chunk-e660.2101cafc.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-e660.feca27c4.js.map rename to priv/static/adminfe/static/js/chunk-e660.2101cafc.js.map index cfc2e08af..2ff5149ad 100644 Binary files a/priv/static/adminfe/static/js/chunk-e660.feca27c4.js.map and b/priv/static/adminfe/static/js/chunk-e660.2101cafc.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-f364.1122502b.js b/priv/static/adminfe/static/js/chunk-f364.1122502b.js deleted file mode 100644 index facad2ed5..000000000 Binary files a/priv/static/adminfe/static/js/chunk-f364.1122502b.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-f364.1122502b.js.map b/priv/static/adminfe/static/js/chunk-f364.1122502b.js.map deleted file mode 100644 index f89dabe30..000000000 Binary files a/priv/static/adminfe/static/js/chunk-f364.1122502b.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-f364.a5927f18.js b/priv/static/adminfe/static/js/chunk-f364.a5927f18.js new file mode 100644 index 000000000..9a872d819 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-f364.a5927f18.js differ diff --git a/priv/static/adminfe/static/js/chunk-f364.a5927f18.js.map b/priv/static/adminfe/static/js/chunk-f364.a5927f18.js.map new file mode 100644 index 000000000..05f67d1a5 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-f364.a5927f18.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-f625.904137fd.js b/priv/static/adminfe/static/js/chunk-f625.904137fd.js deleted file mode 100644 index 053590b28..000000000 Binary files a/priv/static/adminfe/static/js/chunk-f625.904137fd.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-f625.904137fd.js.map b/priv/static/adminfe/static/js/chunk-f625.904137fd.js.map deleted file mode 100644 index 59c1c274e..000000000 Binary files a/priv/static/adminfe/static/js/chunk-f625.904137fd.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-libs.32ea9181.js b/priv/static/adminfe/static/js/chunk-libs.5ca2c8e8.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-libs.32ea9181.js rename to priv/static/adminfe/static/js/chunk-libs.5ca2c8e8.js index 29cfb2b1d..a496b679c 100644 Binary files a/priv/static/adminfe/static/js/chunk-libs.32ea9181.js and b/priv/static/adminfe/static/js/chunk-libs.5ca2c8e8.js differ diff --git a/priv/static/adminfe/static/js/chunk-libs.32ea9181.js.map b/priv/static/adminfe/static/js/chunk-libs.5ca2c8e8.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-libs.32ea9181.js.map rename to priv/static/adminfe/static/js/chunk-libs.5ca2c8e8.js.map index c80cf9acc..3b2db293f 100644 Binary files a/priv/static/adminfe/static/js/chunk-libs.32ea9181.js.map and b/priv/static/adminfe/static/js/chunk-libs.5ca2c8e8.js.map differ diff --git a/priv/static/adminfe/static/js/runtime.6b30c658.js b/priv/static/adminfe/static/js/runtime.6b30c658.js new file mode 100644 index 000000000..ecdf93f1b Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.6b30c658.js differ diff --git a/priv/static/adminfe/static/js/runtime.6b30c658.js.map b/priv/static/adminfe/static/js/runtime.6b30c658.js.map new file mode 100644 index 000000000..67a46d4bf Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.6b30c658.js.map differ diff --git a/priv/static/adminfe/static/js/runtime.ba96836e.js b/priv/static/adminfe/static/js/runtime.ba96836e.js deleted file mode 100644 index 245c7fe20..000000000 Binary files a/priv/static/adminfe/static/js/runtime.ba96836e.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.ba96836e.js.map b/priv/static/adminfe/static/js/runtime.ba96836e.js.map deleted file mode 100644 index f3c5a82af..000000000 Binary files a/priv/static/adminfe/static/js/runtime.ba96836e.js.map and /dev/null differ diff --git a/priv/static/emoji/dino walking.gif b/priv/static/emoji/dino walking.gif new file mode 100644 index 000000000..694a541e7 Binary files /dev/null and b/priv/static/emoji/dino walking.gif differ diff --git a/priv/static/images/avi.png b/priv/static/images/avi.png index c6595adad..df4e2d233 100644 Binary files a/priv/static/images/avi.png and b/priv/static/images/avi.png differ diff --git a/priv/static/images/logo.png b/priv/static/images/logo.png new file mode 100644 index 000000000..7744b1acc Binary files /dev/null and b/priv/static/images/logo.png differ diff --git a/priv/static/index.html b/priv/static/index.html index 9b774959a..88bf1484d 100644 --- a/priv/static/index.html +++ b/priv/static/index.html @@ -1 +1 @@ -Pleroma
\ No newline at end of file +
\ No newline at end of file diff --git a/priv/static/static/emoji.json b/priv/static/static/emoji.json index ae93d17e1..12b91b3f6 100644 --- a/priv/static/static/emoji.json +++ b/priv/static/static/emoji.json @@ -1,969 +1,1431 @@ { - "womans_clothes": "\ud83d\udc5a", - "cookie": "\ud83c\udf6a", - "woman_with_headscarf": "\ud83e\uddd5", - "no_smoking": "\ud83d\udead", - "e-mail": "\ud83d\udce7", - "regional_indicator_d": "\ud83c\udde9", - "oncoming_bus": "\ud83d\ude8d", - "knife": "\ud83d\udd2a", - "person_getting_haircut": "\ud83d\udc87", - "grimacing": "\ud83d\ude2c", - "ophiuchus": "\u26ce", - "regional_indicator_q": "\ud83c\uddf6", - "thinking": "\ud83e\udd14", - "signal_strength": "\ud83d\udcf6", - "cactus": "\ud83c\udf35", - "bullettrain_front": "\ud83d\ude85", - "floppy_disk": "\ud83d\udcbe", - "doughnut": "\ud83c\udf69", - "tv": "\ud83d\udcfa", - "1234": "\ud83d\udd22", - "anguished": "\ud83d\ude27", - "clock1030": "\ud83d\udd65", - "u7533": "\ud83c\ude38", - "speak_no_evil": "\ud83d\ude4a", - "chart_with_upwards_trend": "\ud83d\udcc8", - "trophy": "\ud83c\udfc6", - "musical_score": "\ud83c\udfbc", - "chestnut": "\ud83c\udf30", - "clock1130": "\ud83d\udd66", - "abcd": "\ud83d\udd21", - "syringe": "\ud83d\udc89", - "shrimp": "\ud83e\udd90", - "pisces": "\u2653", - "left_facing_fist": "\ud83e\udd1b", - "bar_chart": "\ud83d\udcca", - "eagle": "\ud83e\udd85", - "woman": "\ud83d\udc69", - "keycap_ten": "\ud83d\udd1f", - "yellow_heart": "\ud83d\udc9b", - "croissant": "\ud83e\udd50", - "mosque": "\ud83d\udd4c", - "rice_ball": "\ud83c\udf59", - "volcano": "\ud83c\udf0b", - "baggage_claim": "\ud83d\udec4", - "family": "\ud83d\udc6a", - "beetle": "\ud83d\udc1e", - "older_adult": "\ud83e\uddd3", - "clock830": "\ud83d\udd63", - "bacon": "\ud83e\udd53", - "sound": "\ud83d\udd09", - "no_bicycles": "\ud83d\udeb3", - "rewind": "\u23ea", - "adult": "\ud83e\uddd1", - "scream_cat": "\ud83d\ude40", - "person_playing_water_polo": "\ud83e\udd3d", - "blue_car": "\ud83d\ude99", - "smiley": "\ud83d\ude03", - "kaaba": "\ud83d\udd4b", - "twisted_rightwards_arrows": "\ud83d\udd00", - "last_quarter_moon": "\ud83c\udf17", - "first_place": "\ud83e\udd47", - "joy_cat": "\ud83d\ude39", - "sleeping": "\ud83d\ude34", - "basketball": "\ud83c\udfc0", - "pray": "\ud83d\ude4f", - "trumpet": "\ud83c\udfba", - "purple_heart": "\ud83d\udc9c", - "broken_heart": "\ud83d\udc94", - "astonished": "\ud83d\ude32", - "soccer": "\u26bd", - "princess": "\ud83d\udc78", - "ant": "\ud83d\udc1c", - "pig": "\ud83d\udc37", - "vhs": "\ud83d\udcfc", - "scream": "\ud83d\ude31", - "mouse": "\ud83d\udc2d", - "field_hockey": "\ud83c\udfd1", - "ab": "\ud83c\udd8e", - "tokyo_tower": "\ud83d\uddfc", - "girl": "\ud83d\udc67", - "u55b6": "\ud83c\ude3a", - "guard": "\ud83d\udc82", - "regional_indicator_s": "\ud83c\uddf8", - "tulip": "\ud83c\udf37", - "capital_abcd": "\ud83d\udd20", - "beginner": "\ud83d\udd30", - "couplekiss": "\ud83d\udc8f", - "u5408": "\ud83c\ude34", - "black_medium_small_square": "\u25fe", - "paperclip": "\ud83d\udcce", - "hedgehog": "\ud83e\udd94", - "musical_note": "\ud83c\udfb5", - "pill": "\ud83d\udc8a", - "blue_heart": "\ud83d\udc99", - "mens": "\ud83d\udeb9", - "third_place": "\ud83e\udd49", - "stew": "\ud83c\udf72", - "prince": "\ud83e\udd34", - "mortar_board": "\ud83c\udf93", - "clock6": "\ud83d\udd55", - "beer": "\ud83c\udf7a", - "person_tipping_hand": "\ud83d\udc81", - "triangular_ruler": "\ud83d\udcd0", - "regional_indicator_y": "\ud83c\uddfe", - "person_facepalming": "\ud83e\udd26", - "steam_locomotive": "\ud83d\ude82", - "fire_engine": "\ud83d\ude92", - "horse": "\ud83d\udc34", - "ribbon": "\ud83c\udf80", - "white_large_square": "\u2b1c", - "smirk": "\ud83d\ude0f", - "genie": "\ud83e\uddde", - "tangerine": "\ud83c\udf4a", - "cl": "\ud83c\udd91", - "japanese_goblin": "\ud83d\udc7a", - "regional_indicator_u": "\ud83c\uddfa", - "ring": "\ud83d\udc8d", - "roller_coaster": "\ud83c\udfa2", - "100": "\ud83d\udcaf", - "clock12": "\ud83d\udd5b", - "two_hearts": "\ud83d\udc95", - "anger": "\ud83d\udca2", - "black_circle": "\u26ab", - "revolving_hearts": "\ud83d\udc9e", - "space_invader": "\ud83d\udc7e", - "bell": "\ud83d\udd14", - "point_up_2": "\ud83d\udc46", - "person_mountain_biking": "\ud83d\udeb5", - "flags": "\ud83c\udf8f", - "pushpin": "\ud83d\udccc", - "large_blue_diamond": "\ud83d\udd37", - "fairy": "\ud83e\uddda", - "european_post_office": "\ud83c\udfe4", - "statue_of_liberty": "\ud83d\uddfd", - "man": "\ud83d\udc68", - "microphone": "\ud83c\udfa4", - "inbox_tray": "\ud83d\udce5", - "bath": "\ud83d\udec0", - "person_gesturing_ok": "\ud83d\ude46", - "clap": "\ud83d\udc4f", - "confused": "\ud83d\ude15", - "fortune_cookie": "\ud83e\udd60", - "kissing_closed_eyes": "\ud83d\ude1a", - "kissing_heart": "\ud83d\ude18", - "tropical_fish": "\ud83d\udc20", - "taco": "\ud83c\udf2e", - "kimono": "\ud83d\udc58", - "u7a7a": "\ud83c\ude33", - "rat": "\ud83d\udc00", - "taurus": "\u2649", - "shopping_cart": "\ud83d\uded2", - "womans_hat": "\ud83d\udc52", - "blossom": "\ud83c\udf3c", - "moyai": "\ud83d\uddff", - "clock130": "\ud83d\udd5c", - "telescope": "\ud83d\udd2d", - "running_shirt_with_sash": "\ud83c\udfbd", - "person_running": "\ud83c\udfc3", - "dizzy": "\ud83d\udcab", - "crescent_moon": "\ud83c\udf19", - "boom": "\ud83d\udca5", - "restroom": "\ud83d\udebb", - "fist": "\u270a", - "white_flower": "\ud83d\udcae", - "clown": "\ud83e\udd21", - "neutral_face": "\ud83d\ude10", - "id": "\ud83c\udd94", - "carrot": "\ud83e\udd55", - "rice_scene": "\ud83c\udf91", - "foggy": "\ud83c\udf01", - "turtle": "\ud83d\udc22", - "mailbox_with_mail": "\ud83d\udcec", - "baseball": "\u26be", - "grin": "\ud83d\ude01", - "bathtub": "\ud83d\udec1", - "feet": "\ud83d\udc3e", - "small_red_triangle": "\ud83d\udd3a", - "camel": "\ud83d\udc2b", - "aquarius": "\u2652", - "face_with_symbols_over_mouth": "\ud83e\udd2c", - "handbag": "\ud83d\udc5c", - "date": "\ud83d\udcc5", - "nail_care": "\ud83d\udc85", - "satellite": "\ud83d\udce1", - "candy": "\ud83c\udf6c", - "white_medium_small_square": "\u25fd", - "clock930": "\ud83d\udd64", - "fearful": "\ud83d\ude28", - "fork_and_knife": "\ud83c\udf74", - "person_wearing_turban": "\ud83d\udc73", - "confounded": "\ud83d\ude16", - "helicopter": "\ud83d\ude81", - "arrow_double_down": "\u23ec", - "convenience_store": "\ud83c\udfea", - "ghost": "\ud83d\udc7b", - "bus": "\ud83d\ude8c", - "waning_gibbous_moon": "\ud83c\udf16", - "bank": "\ud83c\udfe6", - "department_store": "\ud83c\udfec", - "hockey": "\ud83c\udfd2", - "fingers_crossed": "\ud83e\udd1e", - "blond_haired_person": "\ud83d\udc71", - "mag": "\ud83d\udd0d", - "cut_of_meat": "\ud83e\udd69", - "wink": "\ud83d\ude09", - "railway_car": "\ud83d\ude83", - "face_vomiting": "\ud83e\udd2e", - "star_struck": "\ud83e\udd29", - "first_quarter_moon_with_face": "\ud83c\udf1b", - "octagonal_sign": "\ud83d\uded1", - "hospital": "\ud83c\udfe5", - "monkey": "\ud83d\udc12", - "curly_loop": "\u27b0", - "avocado": "\ud83e\udd51", - "earth_americas": "\ud83c\udf0e", - "flashlight": "\ud83d\udd26", - "8ball": "\ud83c\udfb1", - "clock630": "\ud83d\udd61", - "boar": "\ud83d\udc17", - "birthday": "\ud83c\udf82", - "crocodile": "\ud83d\udc0a", - "confetti_ball": "\ud83c\udf8a", - "door": "\ud83d\udeaa", - "school_satchel": "\ud83c\udf92", - "peanuts": "\ud83e\udd5c", - "regional_indicator_m": "\ud83c\uddf2", - "bust_in_silhouette": "\ud83d\udc64", - "sweat_drops": "\ud83d\udca6", - "tongue": "\ud83d\udc45", - "mag_right": "\ud83d\udd0e", - "t_rex": "\ud83e\udd96", - "post_office": "\ud83c\udfe3", - "shell": "\ud83d\udc1a", - "disappointed_relieved": "\ud83d\ude25", - "card_index": "\ud83d\udcc7", - "oncoming_automobile": "\ud83d\ude98", - "passport_control": "\ud83d\udec2", - "cherry_blossom": "\ud83c\udf38", - "shallow_pan_of_food": "\ud83e\udd58", - "heart": "\u2764\ufe0f", - "heartbeat": "\ud83d\udc93", - "crazy_face": "\ud83e\udd2a", - "grapes": "\ud83c\udf47", - "symbols": "\ud83d\udd23", - "gift": "\ud83c\udf81", - "scorpion": "\ud83e\udd82", - "wedding": "\ud83d\udc92", - "last_quarter_moon_with_face": "\ud83c\udf1c", - "love_letter": "\ud83d\udc8c", - "postal_horn": "\ud83d\udcef", - "stuffed_flatbread": "\ud83e\udd59", - "heavy_dollar_sign": "\ud83d\udcb2", - "love_hotel": "\ud83c\udfe9", - "yen": "\ud83d\udcb4", - "person_in_steamy_room": "\ud83e\uddd6", - "palm_tree": "\ud83c\udf34", - "name_badge": "\ud83d\udcdb", - "clock430": "\ud83d\udd5f", - "bike": "\ud83d\udeb2", - "snail": "\ud83d\udc0c", - "bowling": "\ud83c\udfb3", - "umbrella": "\u2614", - "sleeping_accommodation": "\ud83d\udecc", - "fireworks": "\ud83c\udf86", - "closed_book": "\ud83d\udcd5", - "city_sunset": "\ud83c\udf07", - "persevere": "\ud83d\ude23", - "bento": "\ud83c\udf71", - "nut_and_bolt": "\ud83d\udd29", - "page_facing_up": "\ud83d\udcc4", - "snowman": "\u26c4", - "two_women_holding_hands": "\ud83d\udc6d", - "regional_indicator_o": "\ud83c\uddf4", - "calling": "\ud83d\udcf2", - "person_shrugging": "\ud83e\udd37", - "sneezing_face": "\ud83e\udd27", - "arrows_clockwise": "\ud83d\udd03", - "no_pedestrians": "\ud83d\udeb7", - "potato": "\ud83e\udd54", - "cheese": "\ud83e\uddc0", - "full_moon": "\ud83c\udf15", - "mount_fuji": "\ud83d\uddfb", - "sob": "\ud83d\ude2d", - "construction": "\ud83d\udea7", - "head_bandage": "\ud83e\udd15", - "sailboat": "\u26f5", - "slight_frown": "\ud83d\ude41", - "ping_pong": "\ud83c\udfd3", - "hatched_chick": "\ud83d\udc25", - "sun_with_face": "\ud83c\udf1e", - "seedling": "\ud83c\udf31", - "repeat_one": "\ud83d\udd02", - "muscle": "\ud83d\udcaa", - "bridge_at_night": "\ud83c\udf09", - "raised_hands": "\ud83d\ude4c", - "house": "\ud83c\udfe0", - "nerd": "\ud83e\udd13", - "penguin": "\ud83d\udc27", - "peach": "\ud83c\udf51", - "dumpling": "\ud83e\udd5f", - "watch": "\u231a", - "womens": "\ud83d\udeba", - "round_pushpin": "\ud83d\udccd", - "alarm_clock": "\u23f0", - "relieved": "\ud83d\ude0c", - "sagittarius": "\u2650", - "busstop": "\ud83d\ude8f", - "regional_indicator_a": "\ud83c\udde6", - "sandal": "\ud83d\udc61", - "whale2": "\ud83d\udc0b", - "book": "\ud83d\udcd6", - "sweat": "\ud83d\ude13", - "movie_camera": "\ud83c\udfa5", - "clock230": "\ud83d\udd5d", - "tiger": "\ud83d\udc2f", - "tractor": "\ud83d\ude9c", - "smile": "\ud83d\ude04", - "vertical_traffic_light": "\ud83d\udea6", - "exploding_head": "\ud83e\udd2f", - "raised_hand": "\u270b", - "smoking": "\ud83d\udeac", - "page_with_curl": "\ud83d\udcc3", - "exclamation": "\u2757", - "fish": "\ud83d\udc1f", - "mans_shoe": "\ud83d\udc5e", - "sos": "\ud83c\udd98", - "unlock": "\ud83d\udd13", - "dolls": "\ud83c\udf8e", - "ear_of_rice": "\ud83c\udf3e", - "cat2": "\ud83d\udc08", - "u7121": "\ud83c\ude1a", - "repeat": "\ud83d\udd01", - "cool": "\ud83c\udd92", - "minibus": "\ud83d\ude90", - "aerial_tramway": "\ud83d\udea1", - "key": "\ud83d\udd11", - "child": "\ud83e\uddd2", - "camera": "\ud83d\udcf7", - "sunflower": "\ud83c\udf3b", - "white_check_mark": "\u2705", - "white_square_button": "\ud83d\udd33", - "banana": "\ud83c\udf4c", - "milky_way": "\ud83c\udf0c", - "person_gesturing_no": "\ud83d\ude45", - "sushi": "\ud83c\udf63", - "heart_eyes_cat": "\ud83d\ude3b", - "guitar": "\ud83c\udfb8", - "pie": "\ud83e\udd67", - "calendar": "\ud83d\udcc6", - "bear": "\ud83d\udc3b", - "person_in_lotus_position": "\ud83e\uddd8", - "clock10": "\ud83d\udd59", - "top": "\ud83d\udd1d", - "fuelpump": "\u26fd", - "rainbow": "\ud83c\udf08", - "snowboarder": "\ud83c\udfc2", - "drum": "\ud83e\udd41", - "leaves": "\ud83c\udf43", - "first_quarter_moon": "\ud83c\udf13", - "spoon": "\ud83e\udd44", - "pouting_cat": "\ud83d\ude3e", - "shaved_ice": "\ud83c\udf67", - "unamused": "\ud83d\ude12", - "train2": "\ud83d\ude86", - "clock1230": "\ud83d\udd67", - "regional_indicator_r": "\ud83c\uddf7", - "fast_forward": "\u23e9", - "accept": "\ud83c\ude51", - "hammer": "\ud83d\udd28", - "panda_face": "\ud83d\udc3c", - "briefcase": "\ud83d\udcbc", - "package": "\ud83d\udce6", - "flag_black": "\ud83c\udff4", - "smiling_imp": "\ud83d\ude08", - "sunrise_over_mountains": "\ud83c\udf04", - "airplane_departure": "\ud83d\udeeb", - "tiger2": "\ud83d\udc05", - "non-potable_water": "\ud83d\udeb1", - "bird": "\ud83d\udc26", - "barber": "\ud83d\udc88", - "cry": "\ud83d\ude22", - "billed_cap": "\ud83e\udde2", - "pouch": "\ud83d\udc5d", - "link": "\ud83d\udd17", - "zebra": "\ud83e\udd93", - "kiss": "\ud83d\udc8b", - "scorpius": "\u264f", - "prayer_beads": "\ud83d\udcff", - "high_brightness": "\ud83d\udd06", - "kissing_smiling_eyes": "\ud83d\ude19", - "rhino": "\ud83e\udd8f", - "left_luggage": "\ud83d\udec5", - "o": "\u2b55", - "crying_cat_face": "\ud83d\ude3f", - "clock8": "\ud83d\udd57", - "dress": "\ud83d\udc57", - "clock7": "\ud83d\udd56", - "bowl_with_spoon": "\ud83e\udd63", - "rolling_eyes": "\ud83d\ude44", - "fax": "\ud83d\udce0", - "worried": "\ud83d\ude1f", - "grey_question": "\u2754", - "saxophone": "\ud83c\udfb7", - "burrito": "\ud83c\udf2f", - "salad": "\ud83e\udd57", - "regional_indicator_z": "\ud83c\uddff", - "bikini": "\ud83d\udc59", - "milk": "\ud83e\udd5b", - "stars": "\ud83c\udf20", - "lips": "\ud83d\udc44", - "cd": "\ud83d\udcbf", - "weary": "\ud83d\ude29", - "face_with_raised_eyebrow": "\ud83e\udd28", - "lizard": "\ud83e\udd8e", - "tone1": "\ud83c\udffb", - "bullettrain_side": "\ud83d\ude84", - "nose": "\ud83d\udc43", - "innocent": "\ud83d\ude07", - "wilted_rose": "\ud83e\udd40", - "mahjong": "\ud83c\udc04", - "factory": "\ud83c\udfed", - "people_wrestling": "\ud83e\udd3c", - "mailbox": "\ud83d\udceb", - "rage": "\ud83d\ude21", - "wheelchair": "\u267f", - "x": "\u274c", - "flower_playing_cards": "\ud83c\udfb4", - "nauseated_face": "\ud83e\udd22", - "underage": "\ud83d\udd1e", - "ideograph_advantage": "\ud83c\ude50", - "high_heel": "\ud83d\udc60", - "dizzy_face": "\ud83d\ude35", - "stuck_out_tongue": "\ud83d\ude1b", - "mailbox_with_no_mail": "\ud83d\udced", - "orange_heart": "\ud83e\udde1", - "raised_back_of_hand": "\ud83e\udd1a", - "footprints": "\ud83d\udc63", - "notebook_with_decorative_cover": "\ud83d\udcd4", - "mask": "\ud83d\ude37", - "sunglasses": "\ud83d\ude0e", - "pancakes": "\ud83e\udd5e", - "regional_indicator_f": "\ud83c\uddeb", - "dog": "\ud83d\udc36", - "pig2": "\ud83d\udc16", - "ng": "\ud83c\udd96", - "unicorn": "\ud83e\udd84", - "triumph": "\ud83d\ude24", - "eggplant": "\ud83c\udf46", - "egg": "\ud83e\udd5a", - "office": "\ud83c\udfe2", - "goat": "\ud83d\udc10", - "handshake": "\ud83e\udd1d", - "star": "\u2b50", - "rugby_football": "\ud83c\udfc9", - "call_me": "\ud83e\udd19", - "rice_cracker": "\ud83c\udf58", - "droplet": "\ud83d\udca7", - "badminton": "\ud83c\udff8", - "waxing_crescent_moon": "\ud83c\udf12", - "ocean": "\ud83c\udf0a", - "slot_machine": "\ud83c\udfb0", - "wine_glass": "\ud83c\udf77", - "elephant": "\ud83d\udc18", - "blowfish": "\ud83d\udc21", - "ledger": "\ud83d\udcd2", - "money_mouth": "\ud83e\udd11", - "heart_decoration": "\ud83d\udc9f", - "arrow_down_small": "\ud83d\udd3d", - "station": "\ud83d\ude89", - "man_with_chinese_cap": "\ud83d\udc72", - "vampire": "\ud83e\udddb", - "pencil": "\ud83d\udcdd", - "cyclone": "\ud83c\udf00", - "mushroom": "\ud83c\udf44", - "sandwich": "\ud83e\udd6a", - "champagne": "\ud83c\udf7e", - "expressionless": "\ud83d\ude11", - "cold_sweat": "\ud83d\ude30", - "maple_leaf": "\ud83c\udf41", - "dromedary_camel": "\ud83d\udc2a", - "vs": "\ud83c\udd9a", - "person_fencing": "\ud83e\udd3a", - "straight_ruler": "\ud83d\udccf", - "baby_bottle": "\ud83c\udf7c", - "currency_exchange": "\ud83d\udcb1", - "regional_indicator_h": "\ud83c\udded", - "stuck_out_tongue_closed_eyes": "\ud83d\ude1d", - "closed_lock_with_key": "\ud83d\udd10", - "eyes": "\ud83d\udc40", - "water_buffalo": "\ud83d\udc03", - "lock_with_ink_pen": "\ud83d\udd0f", - "heavy_plus_sign": "\u2795", - "bookmark": "\ud83d\udd16", - "soon": "\ud83d\udd1c", - "orange_book": "\ud83d\udcd9", - "pineapple": "\ud83c\udf4d", - "clock9": "\ud83d\udd58", - "small_blue_diamond": "\ud83d\udd39", - "black_large_square": "\u2b1b", - "person_surfing": "\ud83c\udfc4", - "leo": "\u264c", - "merperson": "\ud83e\udddc", - "canoe": "\ud83d\udef6", - "rooster": "\ud83d\udc13", - "hear_no_evil": "\ud83d\ude49", - "corn": "\ud83c\udf3d", - "takeout_box": "\ud83e\udd61", - "oncoming_taxi": "\ud83d\ude96", - "taxi": "\ud83d\ude95", - "chart": "\ud83d\udcb9", - "goal": "\ud83e\udd45", - "melon": "\ud83c\udf48", - "notes": "\ud83c\udfb6", - "sparkler": "\ud83c\udf87", - "dolphin": "\ud83d\udc2c", - "speedboat": "\ud83d\udea4", - "cancer": "\u264b", - "sled": "\ud83d\udef7", - "tanabata_tree": "\ud83c\udf8b", - "train": "\ud83d\ude8b", - "christmas_tree": "\ud83c\udf84", - "two_men_holding_hands": "\ud83d\udc6c", - "back": "\ud83d\udd19", - "balloon": "\ud83c\udf88", - "checkered_flag": "\ud83c\udfc1", - "loop": "\u27bf", - "wc": "\ud83d\udebe", - "jeans": "\ud83d\udc56", - "green_apple": "\ud83c\udf4f", - "crown": "\ud83d\udc51", - "cowboy": "\ud83e\udd20", - "postbox": "\ud83d\udcee", - "volleyball": "\ud83c\udfd0", - "upside_down": "\ud83d\ude43", - "cricket": "\ud83e\udd97", - "custard": "\ud83c\udf6e", - "rose": "\ud83c\udf39", - "eyeglasses": "\ud83d\udc53", - "oncoming_police_car": "\ud83d\ude94", - "atm": "\ud83c\udfe7", - "flying_saucer": "\ud83d\udef8", - "alien": "\ud83d\udc7d", - "hamster": "\ud83d\udc39", - "trident": "\ud83d\udd31", - "disappointed": "\ud83d\ude1e", - "cow": "\ud83d\udc2e", - "police_officer": "\ud83d\udc6e", - "popcorn": "\ud83c\udf7f", - "baby_chick": "\ud83d\udc24", - "video_camera": "\ud83d\udcf9", - "zzz": "\ud83d\udca4", - "person_climbing": "\ud83e\uddd7", - "star2": "\ud83c\udf1f", - "ok": "\ud83c\udd97", - "capricorn": "\u2651", - "chicken": "\ud83d\udc14", - "arrow_double_up": "\u23eb", - "zombie": "\ud83e\udddf", - "closed_umbrella": "\ud83c\udf02", - "person_walking": "\ud83d\udeb6", - "lemon": "\ud83c\udf4b", - "heartpulse": "\ud83d\udc97", - "regional_indicator_i": "\ud83c\uddee", - "sauropod": "\ud83e\udd95", - "u7981": "\ud83c\ude32", - "regional_indicator_w": "\ud83c\uddfc", - "evergreen_tree": "\ud83c\udf32", - "mobile_phone_off": "\ud83d\udcf4", - "koko": "\ud83c\ude01", - "poop": "\ud83d\udca9", - "cup_with_straw": "\ud83e\udd64", - "leopard": "\ud83d\udc06", - "radio_button": "\ud83d\udd18", - "mega": "\ud83d\udce3", - "metal": "\ud83e\udd18", - "shushing_face": "\ud83e\udd2b", - "stuck_out_tongue_winking_eye": "\ud83d\ude1c", - "octopus": "\ud83d\udc19", - "boxing_glove": "\ud83e\udd4a", - "person_juggling": "\ud83e\udd39", - "money_with_wings": "\ud83d\udcb8", - "dollar": "\ud83d\udcb5", - "bride_with_veil": "\ud83d\udc70", - "second_place": "\ud83e\udd48", - "spaghetti": "\ud83c\udf5d", - "waning_crescent_moon": "\ud83c\udf18", - "football": "\ud83c\udfc8", - "white_circle": "\u26aa", - "full_moon_with_face": "\ud83c\udf1d", - "selfie": "\ud83e\udd33", - "tone3": "\ud83c\udffd", - "rabbit": "\ud83d\udc30", - "computer": "\ud83d\udcbb", - "clock11": "\ud83d\udd5a", - "heavy_minus_sign": "\u2796", - "synagogue": "\ud83d\udd4d", - "hourglass": "\u231b", - "gem": "\ud83d\udc8e", - "person_doing_cartwheel": "\ud83e\udd38", - "new_moon_with_face": "\ud83c\udf1a", - "sunrise": "\ud83c\udf05", - "regional_indicator_x": "\ud83c\uddfd", - "open_file_folder": "\ud83d\udcc2", - "gift_heart": "\ud83d\udc9d", - "tada": "\ud83c\udf89", - "green_heart": "\ud83d\udc9a", - "battery": "\ud83d\udd0b", - "regional_indicator_t": "\ud83c\uddf9", - "wrench": "\ud83d\udd27", - "aries": "\u2648", - "man_in_tuxedo": "\ud83e\udd35", - "regional_indicator_e": "\ud83c\uddea", - "regional_indicator_l": "\ud83c\uddf1", - "cake": "\ud83c\udf70", - "clapper": "\ud83c\udfac", - "japanese_castle": "\ud83c\udfef", - "crystal_ball": "\ud83d\udd2e", - "golf": "\u26f3", - "no_mobile_phones": "\ud83d\udcf5", - "person_biking": "\ud83d\udeb4", - "icecream": "\ud83c\udf66", - "mage": "\ud83e\uddd9", - "bookmark_tabs": "\ud83d\udcd1", - "tone4": "\ud83c\udffe", - "mountain_cableway": "\ud83d\udea0", - "person_playing_handball": "\ud83e\udd3e", - "bulb": "\ud83d\udca1", - "clock330": "\ud83d\udd5e", - "metro": "\ud83d\ude87", - "wave": "\ud83d\udc4b", - "whale": "\ud83d\udc33", - "strawberry": "\ud83c\udf53", - "hatching_chick": "\ud83d\udc23", - "trolleybus": "\ud83d\ude8e", - "lollipop": "\ud83c\udf6d", - "clipboard": "\ud83d\udccb", - "point_right": "\ud83d\udc49", - "u6307": "\ud83c\ude2f", - "santa": "\ud83c\udf85", - "hibiscus": "\ud83c\udf3a", - "green_book": "\ud83d\udcd7", - "skull": "\ud83d\udc80", - "tumbler_glass": "\ud83e\udd43", - "clock2": "\ud83d\udd51", - "open_mouth": "\ud83d\ude2e", - "bouquet": "\ud83d\udc90", - "champagne_glass": "\ud83e\udd42", - "poodle": "\ud83d\udc29", - "hushed": "\ud83d\ude2f", - "earth_asia": "\ud83c\udf0f", - "face_with_monocle": "\ud83e\uddd0", - "libra": "\u264e", - "clock5": "\ud83d\udd54", - "ambulance": "\ud83d\ude91", - "u5272": "\ud83c\ude39", - "lipstick": "\ud83d\udc84", - "apple": "\ud83c\udf4e", - "headphones": "\ud83c\udfa7", - "turkey": "\ud83e\udd83", - "pretzel": "\ud83e\udd68", - "bug": "\ud83d\udc1b", - "school": "\ud83c\udfeb", - "speaker": "\ud83d\udd08", - "boot": "\ud83d\udc62", - "cat": "\ud83d\udc31", - "dancer": "\ud83d\udc83", - "no_entry": "\u26d4", - "kissing_cat": "\ud83d\ude3d", - "art": "\ud83c\udfa8", - "coat": "\ud83e\udde5", - "credit_card": "\ud83d\udcb3", - "customs": "\ud83d\udec3", - "broccoli": "\ud83e\udd66", - "point_left": "\ud83d\udc48", - "canned_food": "\ud83e\udd6b", - "sheep": "\ud83d\udc11", - "person_bowing": "\ud83d\ude47", - "scroll": "\ud83d\udcdc", - "martial_arts_uniform": "\ud83e\udd4b", - "amphora": "\ud83c\udffa", - "thought_balloon": "\ud83d\udcad", - "no_bell": "\ud83d\udd15", - "musical_keyboard": "\ud83c\udfb9", - "people_with_bunny_ears_partying": "\ud83d\udc6f", - "european_castle": "\ud83c\udff0", - "punch": "\ud83d\udc4a", - "camera_with_flash": "\ud83d\udcf8", - "regional_indicator_p": "\ud83c\uddf5", - "red_car": "\ud83d\ude97", - "regional_indicator_j": "\ud83c\uddef", - "owl": "\ud83e\udd89", - "chart_with_downwards_trend": "\ud83d\udcc9", - "older_woman": "\ud83d\udc75", - "gemini": "\u264a", - "incoming_envelope": "\ud83d\udce8", - "waxing_gibbous_moon": "\ud83c\udf14", - "toilet": "\ud83d\udebd", - "dragon_face": "\ud83d\udc32", - "koala": "\ud83d\udc28", - "tone5": "\ud83c\udfff", - "kiwi": "\ud83e\udd5d", - "dash": "\ud83d\udca8", - "imp": "\ud83d\udc7f", - "tent": "\u26fa", - "regional_indicator_b": "\ud83c\udde7", - "monorail": "\ud83d\ude9d", - "ox": "\ud83d\udc02", - "giraffe": "\ud83e\udd92", - "new": "\ud83c\udd95", - "person_raising_hand": "\ud83d\ude4b", - "japan": "\ud83d\uddfe", - "rice": "\ud83c\udf5a", - "ticket": "\ud83c\udfab", - "rotating_light": "\ud83d\udea8", - "loudspeaker": "\ud83d\udce2", - "person_getting_massage": "\ud83d\udc86", - "loud_sound": "\ud83d\udd0a", - "hugging": "\ud83e\udd17", - "herb": "\ud83c\udf3f", - "baby": "\ud83d\udc76", - "angel": "\ud83d\udc7c", - "athletic_shoe": "\ud83d\udc5f", - "euro": "\ud83d\udcb6", - "ram": "\ud83d\udc0f", - "large_orange_diamond": "\ud83d\udd36", - "red_circle": "\ud83d\udd34", - "ferris_wheel": "\ud83c\udfa1", - "drooling_face": "\ud83e\udd24", - "microscope": "\ud83d\udd2c", - "middle_finger": "\ud83d\udd95", - "pager": "\ud83d\udcdf", - "pensive": "\ud83d\ude14", - "potable_water": "\ud83d\udeb0", - "abc": "\ud83d\udd24", - "four_leaf_clover": "\ud83c\udf40", - "vulcan": "\ud83d\udd96", - "french_bread": "\ud83e\udd56", - "motor_scooter": "\ud83d\udef5", - "moneybag": "\ud83d\udcb0", - "sparkles": "\u2728", - "gloves": "\ud83e\udde4", - "envelope_with_arrow": "\ud83d\udce9", - "thumbsdown": "\ud83d\udc4e", - "regional_indicator_g": "\ud83c\uddec", - "video_game": "\ud83c\udfae", - "on": "\ud83d\udd1b", - "open_hands": "\ud83d\udc50", - "monkey_face": "\ud83d\udc35", - "mountain_railway": "\ud83d\ude9e", - "bee": "\ud83d\udc1d", - "scooter": "\ud83d\udef4", - "fishing_pole_and_fish": "\ud83c\udfa3", - "smiley_cat": "\ud83d\ude3a", - "heart_eyes": "\ud83d\ude0d", - "horse_racing": "\ud83c\udfc7", - "ear": "\ud83d\udc42", - "blue_circle": "\ud83d\udd35", - "crossed_flags": "\ud83c\udf8c", - "black_joker": "\ud83c\udccf", - "six_pointed_star": "\ud83d\udd2f", - "fountain": "\u26f2", - "free": "\ud83c\udd93", - "tennis": "\ud83c\udfbe", - "yum": "\ud83d\ude0b", - "fried_shrimp": "\ud83c\udf64", - "dragon": "\ud83d\udc09", - "purse": "\ud83d\udc5b", - "clock1": "\ud83d\udd50", - "airplane_arriving": "\ud83d\udeec", - "cucumber": "\ud83e\udd52", - "man_dancing": "\ud83d\udd7a", - "clock730": "\ud83d\udd62", - "deer": "\ud83e\udd8c", - "meat_on_bone": "\ud83c\udf56", - "bomb": "\ud83d\udca3", - "night_with_stars": "\ud83c\udf03", - "snake": "\ud83d\udc0d", - "ramen": "\ud83c\udf5c", - "end": "\ud83d\udd1a", - "do_not_litter": "\ud83d\udeaf", - "joy": "\ud83d\ude02", - "light_rail": "\ud83d\ude88", - "game_die": "\ud83c\udfb2", - "violin": "\ud83c\udfbb", - "tone2": "\ud83c\udffc", - "tropical_drink": "\ud83c\udf79", - "love_you_gesture": "\ud83e\udd1f", - "cherries": "\ud83c\udf52", - "traffic_light": "\ud83d\udea5", - "iphone": "\ud83d\udcf1", - "socks": "\ud83e\udde6", - "wind_chime": "\ud83c\udf90", - "no_entry_sign": "\ud83d\udeab", - "elf": "\ud83e\udddd", - "squid": "\ud83e\udd91", - "person_pouting": "\ud83d\ude4e", - "smile_cat": "\ud83d\ude38", - "beers": "\ud83c\udf7b", - "minidisc": "\ud83d\udcbd", - "clock4": "\ud83d\udd53", - "ice_cream": "\ud83c\udf68", - "cocktail": "\ud83c\udf78", - "clock3": "\ud83d\udd52", - "frowning": "\ud83d\ude26", - "hamburger": "\ud83c\udf54", - "brain": "\ud83e\udde0", - "heavy_division_sign": "\u2797", - "tophat": "\ud83c\udfa9", - "no_mouth": "\ud83d\ude36", - "ski": "\ud83c\udfbf", - "right_facing_fist": "\ud83e\udd1c", - "mailbox_closed": "\ud83d\udcea", - "chocolate_bar": "\ud83c\udf6b", - "rabbit2": "\ud83d\udc07", - "honey_pot": "\ud83c\udf6f", - "izakaya_lantern": "\ud83c\udfee", - "articulated_lorry": "\ud83d\ude9b", - "face_with_hand_over_mouth": "\ud83e\udd2d", - "japanese_ogre": "\ud83d\udc79", - "zap": "\u26a1", - "rocket": "\ud83d\ude80", - "pizza": "\ud83c\udf55", - "pound": "\ud83d\udcb7", - "person_swimming": "\ud83c\udfca", - "anchor": "\u2693", - "coconut": "\ud83e\udd65", - "sparkling_heart": "\ud83d\udc96", - "older_man": "\ud83d\udc74", - "mouse2": "\ud83d\udc01", - "angry": "\ud83d\ude20", - "up": "\ud83c\udd99", - "gorilla": "\ud83e\udd8d", - "children_crossing": "\ud83d\udeb8", - "smirk_cat": "\ud83d\ude3c", - "pregnant_woman": "\ud83e\udd30", - "electric_plug": "\ud83d\udd0c", - "dog2": "\ud83d\udc15", - "question": "\u2753", - "carousel_horse": "\ud83c\udfa0", - "church": "\u26ea", - "outbox_tray": "\ud83d\udce4", - "cinema": "\ud83c\udfa6", - "flushed": "\ud83d\ude33", - "blush": "\ud83d\ude0a", - "medal": "\ud83c\udfc5", - "coffee": "\u2615", - "gun": "\ud83d\udd2b", - "city_dusk": "\ud83c\udf06", - "watermelon": "\ud83c\udf49", - "cricket_game": "\ud83c\udfcf", - "shower": "\ud83d\udebf", - "mute": "\ud83d\udd07", - "breast_feeding": "\ud83e\udd31", - "sweat_smile": "\ud83d\ude05", - "construction_worker": "\ud83d\udc77", - "cow2": "\ud83d\udc04", - "arrows_counterclockwise": "\ud83d\udd04", - "u6e80": "\ud83c\ude35", - "grinning": "\ud83d\ude00", - "globe_with_meridians": "\ud83c\udf10", - "diamond_shape_with_a_dot_inside": "\ud83d\udca0", - "deciduous_tree": "\ud83c\udf33", - "shark": "\ud83e\udd88", - "tram": "\ud83d\ude8a", - "person_rowing_boat": "\ud83d\udea3", - "chopsticks": "\ud83e\udd62", - "black_heart": "\ud83d\udda4", - "seat": "\ud83d\udcba", - "kissing": "\ud83d\ude17", - "laughing": "\ud83d\ude06", - "slight_smile": "\ud83d\ude42", - "radio": "\ud83d\udcfb", - "arrow_up_small": "\ud83d\udd3c", - "dango": "\ud83c\udf61", - "rofl": "\ud83e\udd23", - "see_no_evil": "\ud83d\ude48", - "thermometer_face": "\ud83e\udd12", - "hotdog": "\ud83c\udf2d", - "virgo": "\u264d", - "poultry_leg": "\ud83c\udf57", - "hotel": "\ud83c\udfe8", - "wolf": "\ud83d\udc3a", - "curry": "\ud83c\udf5b", - "regional_indicator_v": "\ud83c\uddfb", - "crab": "\ud83e\udd80", - "tired_face": "\ud83d\ude2b", - "place_of_worship": "\ud83d\uded0", - "ok_hand": "\ud83d\udc4c", - "speech_balloon": "\ud83d\udcac", - "sleepy": "\ud83d\ude2a", - "earth_africa": "\ud83c\udf0d", - "police_car": "\ud83d\ude93", - "small_red_triangle_down": "\ud83d\udd3b", - "bearded_person": "\ud83e\uddd4", - "curling_stone": "\ud83e\udd4c", - "scarf": "\ud83e\udde3", - "fire": "\ud83d\udd25", - "file_folder": "\ud83d\udcc1", - "zipper_mouth": "\ud83e\udd10", - "new_moon": "\ud83c\udf11", - "regional_indicator_n": "\ud83c\uddf3", - "negative_squared_cross_mark": "\u274e", - "newspaper": "\ud83d\udcf0", - "dvd": "\ud83d\udcc0", - "pear": "\ud83c\udf50", - "partly_sunny": "\u26c5", - "black_square_button": "\ud83d\udd32", - "low_brightness": "\ud83d\udd05", - "sake": "\ud83c\udf76", - "bow_and_arrow": "\ud83c\udff9", - "cooking": "\ud83c\udf73", - "fish_cake": "\ud83c\udf65", - "tomato": "\ud83c\udf45", - "couple_with_heart": "\ud83d\udc91", - "telephone_receiver": "\ud83d\udcde", - "triangular_flag_on_post": "\ud83d\udea9", - "jack_o_lantern": "\ud83c\udf83", - "blue_book": "\ud83d\udcd8", - "clock530": "\ud83d\udd60", - "u6709": "\ud83c\ude36", - "palms_up_together": "\ud83e\udd32", - "lion_face": "\ud83e\udd81", - "lock": "\ud83d\udd12", - "duck": "\ud83e\udd86", - "truck": "\ud83d\ude9a", - "oden": "\ud83c\udf62", - "busts_in_silhouette": "\ud83d\udc65", - "hourglass_flowing_sand": "\u23f3", - "frog": "\ud83d\udc38", - "fox": "\ud83e\udd8a", - "bread": "\ud83c\udf5e", - "put_litter_in_its_place": "\ud83d\udeae", - "couple": "\ud83d\udc6b", - "bamboo": "\ud83c\udf8d", - "regional_indicator_c": "\ud83c\udde8", - "menorah": "\ud83d\udd4e", - "circus_tent": "\ud83c\udfaa", - "lying_face": "\ud83e\udd25", - "small_orange_diamond": "\ud83d\udd38", - "ship": "\ud83d\udea2", - "person_frowning": "\ud83d\ude4d", - "racehorse": "\ud83d\udc0e", - "thumbsup": "\ud83d\udc4d", - "cupid": "\ud83d\udc98", - "robot": "\ud83e\udd16", - "fallen_leaf": "\ud83c\udf42", - "pig_nose": "\ud83d\udc3d", - "vibration_mode": "\ud83d\udcf3", - "necktie": "\ud83d\udc54", - "boy": "\ud83d\udc66", - "house_with_garden": "\ud83c\udfe1", - "point_down": "\ud83d\udc47", - "grey_exclamation": "\u2755", - "books": "\ud83d\udcda", - "regional_indicator_k": "\ud83c\uddf0", - "shirt": "\ud83d\udc55", - "fries": "\ud83c\udf5f", - "dart": "\ud83c\udfaf", - "tea": "\ud83c\udf75", - "mrs_claus": "\ud83e\udd36", - "suspension_railway": "\ud83d\ude9f", - "baby_symbol": "\ud83d\udebc", - "sweet_potato": "\ud83c\udf60", - "butterfly": "\ud83e\udd8b", - "performing_arts": "\ud83c\udfad", - "notebook": "\ud83d\udcd3", - "bat": "\ud83e\udd87" -} + "100": "💯", + "1234": "🔢", + "1st_place_medal": "🥇", + "2nd_place_medal": "🥈", + "3rd_place_medal": "🥉", + "8ball": "🎱", + "a_button_blood_type": "🅰", + "ab": "🆎", + "abacus": "🧮", + "abc": "🔤", + "abcd": "🔡", + "accept": "🉑", + "adhesive_bandage": "🩹", + "admission_tickets": "🎟", + "adult": "🧑", + "aerial_tramway": "🚡", + "airplane": "✈", + "airplane_arriving": "🛬", + "airplane_departure": "🛫", + "alarm_clock": "⏰", + "alembic": "⚗️", + "alien": "👽", + "ambulance": "🚑", + "amphora": "🏺", + "anchor": "⚓", + "angel": "👼", + "anger": "💢", + "anger_right": "🗯", + "angry": "😠", + "anguished": "😧", + "ant": "🐜", + "apple": "🍎", + "aquarius": "♒", + "aries": "♈", + "arrow_backward": "◀️", + "arrow_double_down": "⏬", + "arrow_double_up": "⏫", + "arrow_down": "⬇️", + "arrow_down_small": "🔽", + "arrow_forward": "▶️", + "arrow_heading_down": "⤵️", + "arrow_heading_up": "⤴️", + "arrow_left": "⬅️", + "arrow_lower_left": "↙️", + "arrow_lower_right": "↘️", + "arrow_right": "➡", + "arrow_right_hook": "↪️", + "arrow_up": "⬆️", + "arrow_up_down": "↕", + "arrow_up_small": "🔼", + "arrow_upper_left": "↖", + "arrow_upper_right": "↗️", + "arrows_clockwise": "🔃", + "arrows_counterclockwise": "🔄", + "art": "🎨", + "articulated_lorry": "🚛", + "artist_palette": "🎨", + "asterisk": "*⃣", + "astonished": "😲", + "athletic_shoe": "👟", + "atm": "🏧", + "atom": "⚛", + "atom_symbol": "⚛️", + "auto_rickshaw": "🛺", + "automobile": "🚗", + "avocado": "🥑", + "axe": "🪓", + "b_button_blood_type": "🅱", + "baby": "👶", + "baby_bottle": "🍼", + "baby_chick": "🐤", + "baby_symbol": "🚼", + "back": "🔙", + "bacon": "🥓", + "badger": "🦡", + "badminton": "🏸", + "bagel": "🥯", + "baggage_claim": "🛄", + "baguette_bread": "🥖", + "balance_scale": "⚖️", + "bald": "🦲", + "ballet_shoes": "🩰", + "balloon": "🎈", + "ballot_box": "🗳", + "ballot_box_with_check": "☑️", + "bamboo": "🎍", + "banana": "🍌", + "bangbang": "‼️", + "banjo": "🪕", + "bank": "🏦", + "bar_chart": "📊", + "barber": "💈", + "baseball": "⚾", + "basket": "🧺", + "basketball": "🏀", + "basketballer": "⛹", + "bat": "🦇", + "bath": "🛀", + "bathtub": "🛁", + "battery": "🔋", + "beach_umbrella": "⛱", + "beach_with_umbrella": "🏖", + "bear": "🐻", + "beard": "🧔", + "bearded_person": "🧔", + "bed": "🛏", + "bee": "🐝", + "beer": "🍺", + "beers": "🍻", + "beetle": "🐞", + "beginner": "🔰", + "bell": "🔔", + "bellhop_bell": "🛎", + "bento": "🍱", + "beverage_box": "🧃", + "bicyclist": "🚴", + "bike": "🚲", + "bikini": "👙", + "billed_cap": "🧢", + "biohazard": "☣️", + "bird": "🐦", + "birthday": "🎂", + "black_circle": "⚫", + "black_heart": "🖤", + "black_joker": "🃏", + "black_large_square": "⬛", + "black_medium_small_square": "◾", + "black_medium_square": "◼", + "black_nib": "✒️", + "black_small_square": "▪", + "black_square_button": "🔲", + "blond_haired_person": "👱", + "blossom": "🌼", + "blowfish": "🐡", + "blue_book": "📘", + "blue_car": "🚙", + "blue_circle": "🔵", + "blue_heart": "💙", + "blue_square": "🟦", + "blush": "😊", + "boar": "🐗", + "bomb": "💣", + "bone": "🦴", + "book": "📖", + "bookmark": "🔖", + "bookmark_tabs": "📑", + "books": "📚", + "boom": "💥", + "boot": "👢", + "bouquet": "💐", + "bow": "🙇", + "bow_and_arrow": "🏹", + "bowl_with_spoon": "🥣", + "bowling": "🎳", + "boxing_glove": "🥊", + "boy": "👦", + "brain": "🧠", + "bread": "🍞", + "breast_feeding": "🤱", + "breastfeeding": "🤱", + "brick": "🧱", + "bride_with_veil": "👰", + "bridge_at_night": "🌉", + "briefcase": "💼", + "briefs": "🩲", + "broccoli": "🥦", + "broken_heart": "💔", + "broom": "🧹", + "brown_circle": "🟤", + "brown_heart": "🤎", + "bug": "🐛", + "building_construction": "🏗", + "bulb": "💡", + "bullettrain_front": "🚅", + "bullettrain_side": "🚄", + "burrito": "🌯", + "bus": "🚌", + "busstop": "🚏", + "bust_in_silhouette": "👤", + "busts_in_silhouette": "👥", + "butter": "🧈", + "butterfly": "🦋", + "cactus": "🌵", + "cake": "🍰", + "calendar": "📆", + "call_me": "🤙", + "call_me_hand": "🤙", + "calling": "📲", + "camel": "🐫", + "camera": "📷", + "camera_with_flash": "📸", + "camping": "🏕", + "cancer": "♋", + "candle": "🕯", + "candy": "🍬", + "canned_food": "🥫", + "canoe": "🛶", + "capital_abcd": "🔠", + "capricorn": "♑", + "card_file_box": "🗃", + "card_index": "📇", + "card_index_dividers": "🗂", + "carousel_horse": "🎠", + "carrot": "🥕", + "cat": "🐱", + "cat2": "🐈", + "cd": "💿", + "chains": "⛓️", + "chair": "🪑", + "champagne": "🍾", + "champagne_glass": "🥂", + "chart": "💹", + "chart_with_downwards_trend": "📉", + "chart_with_upwards_trend": "📈", + "check_box_with_check": "☑", + "check_mark": "✔", + "checkered_flag": "🏁", + "cheese": "🧀", + "cheese_wedge": "🧀", + "cherries": "🍒", + "cherry_blossom": "🌸", + "chess_pawn": "♟", + "chestnut": "🌰", + "chicken": "🐔", + "child": "🧒", + "children_crossing": "🚸", + "chipmunk": "🐿", + "chocolate_bar": "🍫", + "chopsticks": "🥢", + "christmas_tree": "🎄", + "church": "⛪", + "cinema": "🎦", + "circled_m": "Ⓜ", + "circus_tent": "🎪", + "city_dusk": "🌆", + "city_sunset": "🌇", + "cityscape": "🏙", + "cityscape_at_dusk": "🌆", + "cl": "🆑", + "clap": "👏", + "clapper": "🎬", + "classical_building": "🏛", + "clinking_glasses": "🥂", + "clipboard": "📋", + "clock1": "🕐", + "clock10": "🕙", + "clock1030": "🕥", + "clock11": "🕚", + "clock1130": "🕦", + "clock12": "🕛", + "clock1230": "🕧", + "clock130": "🕜", + "clock2": "🕑", + "clock230": "🕝", + "clock3": "🕒", + "clock330": "🕞", + "clock4": "🕓", + "clock430": "🕟", + "clock5": "🕔", + "clock530": "🕠", + "clock6": "🕕", + "clock630": "🕡", + "clock7": "🕖", + "clock730": "🕢", + "clock8": "🕗", + "clock830": "🕣", + "clock9": "🕘", + "clock930": "🕤", + "closed_book": "📕", + "closed_lock_with_key": "🔐", + "closed_umbrella": "🌂", + "cloud": "☁️", + "cloud_with_lightning": "🌩", + "cloud_with_lightning_and_rain": "⛈️", + "cloud_with_rain": "🌧", + "cloud_with_snow": "🌨", + "clown": "🤡", + "clown_face": "🤡", + "club_suit": "♣️", + "clubs": "♣", + "coat": "🧥", + "cocktail": "🍸", + "coconut": "🥥", + "coffee": "☕", + "coffin": "⚰️", + "cold_face": "🥶", + "cold_sweat": "😰", + "comet": "☄️", + "compass": "🧭", + "compression": "🗜", + "computer": "💻", + "computer_mouse": "🖱", + "confetti_ball": "🎊", + "confounded": "😖", + "confused": "😕", + "congratulations": "㊗", + "construction": "🚧", + "construction_worker": "👷", + "control_knobs": "🎛", + "convenience_store": "🏪", + "cookie": "🍪", + "cooking": "🍳", + "cool": "🆒", + "cop": "👮", + "copyright": "©", + "corn": "🌽", + "couch_and_lamp": "🛋", + "couple": "👫", + "couple_with_heart": "💑", + "couplekiss": "💏", + "cow": "🐮", + "cow2": "🐄", + "cowboy": "🤠", + "cowboy_hat_face": "🤠", + "crab": "🦀", + "crayon": "🖍", + "crazy_face": "🤪", + "credit_card": "💳", + "crescent_moon": "🌙", + "cricket": "🦗", + "cricket_game": "🏏", + "crocodile": "🐊", + "croissant": "🥐", + "cross": "✝️", + "crossed_fingers": "🤞", + "crossed_flags": "🎌", + "crossed_swords": "⚔️", + "crown": "👑", + "cry": "😢", + "crying_cat_face": "😿", + "crystal_ball": "🔮", + "cucumber": "🥒", + "cup_with_straw": "🥤", + "cupcake": "🧁", + "cupid": "💘", + "curling_stone": "🥌", + "curly_hair": "🦱", + "curly_loop": "➰", + "currency_exchange": "💱", + "curry": "🍛", + "custard": "🍮", + "customs": "🛃", + "cut_of_meat": "🥩", + "cyclone": "🌀", + "dagger": "🗡", + "dancer": "💃", + "dancers": "👯", + "dango": "🍡", + "dark_skin_tone": "🏿", + "dark_sunglasses": "🕶", + "dart": "🎯", + "dash": "💨", + "date": "📅", + "deaf_person": "🧏", + "deciduous_tree": "🌳", + "deer": "🦌", + "department_store": "🏬", + "derelict_house": "🏚", + "desert": "🏜", + "desert_island": "🏝", + "desktop_computer": "🖥", + "detective": "🕵", + "diamond_shape_with_a_dot_inside": "💠", + "diamond_suit": "♦️", + "diamonds": "♦", + "disappointed": "😞", + "disappointed_relieved": "😥", + "diving_mask": "🤿", + "diya_lamp": "🪔", + "dizzy": "💫", + "dizzy_face": "😵", + "dna": "🧬", + "do_not_litter": "🚯", + "dog": "🐶", + "dog2": "🐕", + "dollar": "💵", + "dolls": "🎎", + "dolphin": "🐬", + "door": "🚪", + "double_exclamation_mark": "‼", + "doughnut": "🍩", + "dove": "🕊", + "down_arrow": "⬇", + "downleft_arrow": "↙", + "downright_arrow": "↘", + "dragon": "🐉", + "dragon_face": "🐲", + "dress": "👗", + "dromedary_camel": "🐪", + "drooling_face": "🤤", + "drop_of_blood": "🩸", + "droplet": "💧", + "drum": "🥁", + "duck": "🦆", + "dumpling": "🥟", + "dvd": "📀", + "e-mail": "📧", + "eagle": "🦅", + "ear": "👂", + "ear_of_rice": "🌾", + "ear_with_hearing_aid": "🦻", + "earth_africa": "🌍", + "earth_americas": "🌎", + "earth_asia": "🌏", + "egg": "🥚", + "eggplant": "🍆", + "eight": "8⃣", + "eight_pointed_black_star": "✴️", + "eight_spoked_asterisk": "✳️", + "eightpointed_star": "✴", + "eightspoked_asterisk": "✳", + "eject_button": "⏏", + "electric_plug": "🔌", + "elephant": "🐘", + "elf": "🧝", + "end": "🔚", + "envelope": "✉", + "envelope_with_arrow": "📩", + "euro": "💶", + "european_castle": "🏰", + "european_post_office": "🏤", + "evergreen_tree": "🌲", + "exclamation": "❗", + "exclamation_question_mark": "⁉", + "exploding_head": "🤯", + "expressionless": "😑", + "eye": "👁", + "eyeglasses": "👓", + "eyes": "👀", + "face_vomiting": "🤮", + "face_with_hand_over_mouth": "🤭", + "face_with_headbandage": "🤕", + "face_with_monocle": "🧐", + "face_with_raised_eyebrow": "🤨", + "face_with_symbols_on_mouth": "🤬", + "face_with_symbols_over_mouth": "🤬", + "face_with_thermometer": "🤒", + "factory": "🏭", + "fairy": "🧚", + "falafel": "🧆", + "fallen_leaf": "🍂", + "family": "👪", + "fast_forward": "⏩", + "fax": "📠", + "fearful": "😨", + "feet": "🐾", + "female_sign": "♀", + "ferris_wheel": "🎡", + "ferry": "⛴️", + "field_hockey": "🏑", + "file_cabinet": "🗄", + "file_folder": "📁", + "film_frames": "🎞", + "film_projector": "📽", + "fingers_crossed": "🤞", + "fire": "🔥", + "fire_engine": "🚒", + "fire_extinguisher": "🧯", + "firecracker": "🧨", + "fireworks": "🎆", + "first_place": "🥇", + "first_quarter_moon": "🌓", + "first_quarter_moon_with_face": "🌛", + "fish": "🐟", + "fish_cake": "🍥", + "fishing_pole_and_fish": "🎣", + "fist": "✊", + "five": "5⃣", + "flag_black": "🏴", + "flag_white": "🏳", + "flags": "🎏", + "flamingo": "🦩", + "flashlight": "🔦", + "flat_shoe": "🥿", + "fleur-de-lis": "⚜", + "fleurde-lis": "⚜️", + "floppy_disk": "💾", + "flower_playing_cards": "🎴", + "flushed": "😳", + "flying_disc": "🥏", + "flying_saucer": "🛸", + "fog": "🌫", + "foggy": "🌁", + "foot": "🦶", + "football": "🏈", + "footprints": "👣", + "fork_and_knife": "🍴", + "fork_and_knife_with_plate": "🍽", + "fortune_cookie": "🥠", + "fountain": "⛲", + "fountain_pen": "🖋", + "four": "4⃣", + "four_leaf_clover": "🍀", + "fox": "🦊", + "framed_picture": "🖼", + "free": "🆓", + "french_bread": "🥖", + "fried_shrimp": "🍤", + "fries": "🍟", + "frog": "🐸", + "frowning": "😦", + "frowning_face": "☹️", + "fuelpump": "⛽", + "full_moon": "🌕", + "full_moon_with_face": "🌝", + "funeral_urn": "⚱️", + "game_die": "🎲", + "garlic": "🧄", + "gear": "⚙️", + "gem": "💎", + "gemini": "♊", + "genie": "🧞", + "ghost": "👻", + "gift": "🎁", + "gift_heart": "💝", + "giraffe": "🦒", + "girl": "👧", + "glass_of_milk": "🥛", + "globe_with_meridians": "🌐", + "gloves": "🧤", + "goal": "🥅", + "goal_net": "🥅", + "goat": "🐐", + "goggles": "🥽", + "golf": "⛳", + "golfer": "🏌", + "gorilla": "🦍", + "grapes": "🍇", + "green_apple": "🍏", + "green_book": "📗", + "green_circle": "🟢", + "green_heart": "💚", + "green_salad": "🥗", + "green_square": "🟩", + "grey_exclamation": "❕", + "grey_question": "❔", + "grimacing": "😬", + "grin": "😁", + "grinning": "😀", + "guard": "💂", + "guardsman": "💂", + "guide_dog": "🦮", + "guitar": "🎸", + "gun": "🔫", + "haircut": "💇", + "hamburger": "🍔", + "hammer": "🔨", + "hammer_and_pick": "⚒️", + "hammer_and_wrench": "🛠", + "hamster": "🐹", + "hand_with_fingers_splayed": "🖐", + "handbag": "👜", + "handshake": "🤝", + "hash": "#⃣", + "hatched_chick": "🐥", + "hatching_chick": "🐣", + "head_bandage": "🤕", + "headphones": "🎧", + "hear_no_evil": "🙉", + "heart": "❤️", + "heart_decoration": "💟", + "heart_exclamation": "❣", + "heart_eyes": "😍", + "heart_eyes_cat": "😻", + "heart_suit": "♥️", + "heartbeat": "💓", + "heartpulse": "💗", + "hearts": "♥", + "heavy_check_mark": "✔️", + "heavy_division_sign": "➗", + "heavy_dollar_sign": "💲", + "heavy_minus_sign": "➖", + "heavy_multiplication_x": "✖️", + "heavy_plus_sign": "➕", + "hedgehog": "🦔", + "helicopter": "🚁", + "herb": "🌿", + "hibiscus": "🌺", + "high_brightness": "🔆", + "high_heel": "👠", + "hiking_boot": "🥾", + "hindu_temple": "🛕", + "hippopotamus": "🦛", + "hockey": "🏒", + "hole": "🕳", + "honey_pot": "🍯", + "horse": "🐴", + "horse_racing": "🏇", + "hospital": "🏥", + "hot_face": "🥵", + "hot_pepper": "🌶", + "hot_springs": "♨", + "hotdog": "🌭", + "hotel": "🏨", + "hotsprings": "♨️", + "hourglass": "⌛", + "hourglass_flowing_sand": "⏳", + "house": "🏠", + "house_with_garden": "🏡", + "houses": "🏘", + "hugging": "🤗", + "hundred_points": "💯", + "hushed": "😯", + "ice": "🧊", + "ice_cream": "🍨", + "ice_hockey": "🏒", + "ice_skate": "⛸️", + "icecream": "🍦", + "id": "🆔", + "ideograph_advantage": "🉐", + "imp": "👿", + "inbox_tray": "📥", + "incoming_envelope": "📨", + "index_pointing_up": "☝", + "infinity": "♾", + "information": "ℹ️", + "information_desk_person": "💁", + "information_source": "ℹ", + "innocent": "😇", + "input_numbers": "🔢", + "interrobang": "⁉️", + "iphone": "📱", + "izakaya_lantern": "🏮", + "jack_o_lantern": "🎃", + "japan": "🗾", + "japanese_castle": "🏯", + "japanese_congratulations_button": "㊗️", + "japanese_free_of_charge_button": "🈚", + "japanese_goblin": "👺", + "japanese_ogre": "👹", + "japanese_reserved_button": "🈯", + "japanese_secret_button": "㊙️", + "japanese_service_charge_button": "🈂", + "jeans": "👖", + "joy": "😂", + "joy_cat": "😹", + "joystick": "🕹", + "kaaba": "🕋", + "kangaroo": "🦘", + "key": "🔑", + "keyboard": "⌨️", + "keycap_ten": "🔟", + "kick_scooter": "🛴", + "kimono": "👘", + "kiss": "💋", + "kissing": "😗", + "kissing_cat": "😽", + "kissing_closed_eyes": "😚", + "kissing_heart": "😘", + "kissing_smiling_eyes": "😙", + "kitchen_knife": "🔪", + "kite": "🪁", + "kiwi": "🥝", + "kiwi_fruit": "🥝", + "knife": "🔪", + "koala": "🐨", + "koko": "🈁", + "lab_coat": "🥼", + "label": "🏷", + "lacrosse": "🥍", + "large_blue_diamond": "🔷", + "large_orange_diamond": "🔶", + "last_quarter_moon": "🌗", + "last_quarter_moon_with_face": "🌜", + "last_track_button": "⏮️", + "latin_cross": "✝", + "laughing": "😆", + "leafy_green": "🥬", + "leaves": "🍃", + "ledger": "📒", + "left_arrow": "⬅", + "left_arrow_curving_right": "↪", + "left_facing_fist": "🤛", + "left_luggage": "🛅", + "left_right_arrow": "↔", + "leftfacing_fist": "🤛", + "leftright_arrow": "↔️", + "leftwards_arrow_with_hook": "↩️", + "leg": "🦵", + "lemon": "🍋", + "leo": "♌", + "leopard": "🐆", + "level_slider": "🎚", + "libra": "♎", + "light_rail": "🚈", + "light_skin_tone": "🏻", + "link": "🔗", + "linked_paperclips": "🖇", + "lion_face": "🦁", + "lips": "👄", + "lipstick": "💄", + "lizard": "🦎", + "llama": "🦙", + "lobster": "🦞", + "lock": "🔒", + "lock_with_ink_pen": "🔏", + "lollipop": "🍭", + "loop": "➿", + "lotion_bottle": "🧴", + "loud_sound": "🔊", + "loudspeaker": "📢", + "love_hotel": "🏩", + "love_letter": "💌", + "love_you_gesture": "🤟", + "loveyou_gesture": "🤟", + "low_brightness": "🔅", + "luggage": "🧳", + "lying_face": "🤥", + "m": "Ⓜ️", + "mag": "🔍", + "mag_right": "🔎", + "mage": "🧙", + "magnet": "🧲", + "mahjong": "🀄", + "mailbox": "📫", + "mailbox_closed": "📪", + "mailbox_with_mail": "📬", + "mailbox_with_no_mail": "📭", + "male_sign": "♂", + "man": "👨", + "man_dancing": "🕺", + "man_in_suit": "🕴", + "man_in_tuxedo": "🤵", + "man_with_chinese_cap": "👲", + "man_with_gua_pi_mao": "👲", + "man_with_turban": "👳", + "mango": "🥭", + "mans_shoe": "👞", + "mantelpiece_clock": "🕰", + "manual_wheelchair": "🦽", + "maple_leaf": "🍁", + "martial_arts_uniform": "🥋", + "mask": "😷", + "massage": "💆", + "mate": "🧉", + "meat_on_bone": "🍖", + "mechanical_arm": "🦾", + "mechanical_leg": "🦿", + "medal": "🏅", + "medical_symbol": "⚕", + "medium_skin_tone": "🏽", + "mediumdark_skin_tone": "🏾", + "mediumlight_skin_tone": "🏼", + "mega": "📣", + "melon": "🍈", + "memo": "📝", + "menorah": "🕎", + "mens": "🚹", + "merperson": "🧜", + "metal": "🤘", + "metro": "🚇", + "microbe": "🦠", + "microphone": "🎤", + "microscope": "🔬", + "middle_finger": "🖕", + "military_medal": "🎖", + "milk": "🥛", + "milky_way": "🌌", + "minibus": "🚐", + "minidisc": "💽", + "mobile_phone_off": "📴", + "money_mouth": "🤑", + "money_with_wings": "💸", + "moneybag": "💰", + "moneymouth_face": "🤑", + "monkey": "🐒", + "monkey_face": "🐵", + "monorail": "🚝", + "moon_cake": "🥮", + "mortar_board": "🎓", + "mosque": "🕌", + "mosquito": "🦟", + "motor_boat": "🛥", + "motor_scooter": "🛵", + "motorcycle": "🏍", + "motorized_wheelchair": "🦼", + "motorway": "🛣", + "mount_fuji": "🗻", + "mountain": "⛰️", + "mountain_bicyclist": "🚵", + "mountain_cableway": "🚠", + "mountain_railway": "🚞", + "mouse": "🐭", + "mouse2": "🐁", + "movie_camera": "🎥", + "moyai": "🗿", + "mrs_claus": "🤶", + "multiplication_sign": "✖", + "muscle": "💪", + "mushroom": "🍄", + "musical_keyboard": "🎹", + "musical_note": "🎵", + "musical_score": "🎼", + "mute": "🔇", + "nail_care": "💅", + "name_badge": "📛", + "national_park": "🏞", + "nauseated_face": "🤢", + "nazar_amulet": "🧿", + "necktie": "👔", + "negative_squared_cross_mark": "❎", + "nerd": "🤓", + "neutral_face": "😐", + "new": "🆕", + "new_moon": "🌑", + "new_moon_with_face": "🌚", + "newspaper": "📰", + "next_track_button": "⏭️", + "ng": "🆖", + "night_with_stars": "🌃", + "nine": "9⃣", + "no_bell": "🔕", + "no_bicycles": "🚳", + "no_entry": "⛔", + "no_entry_sign": "🚫", + "no_good": "🙅", + "no_mobile_phones": "📵", + "no_mouth": "😶", + "no_pedestrians": "🚷", + "no_smoking": "🚭", + "non-potable_water": "🚱", + "nose": "👃", + "notebook": "📓", + "notebook_with_decorative_cover": "📔", + "notes": "🎶", + "nut_and_bolt": "🔩", + "o": "⭕", + "o_button_blood_type": "🅾", + "ocean": "🌊", + "octagonal_sign": "🛑", + "octopus": "🐙", + "oden": "🍢", + "office": "🏢", + "oil_drum": "🛢", + "ok": "🆗", + "ok_hand": "👌", + "ok_woman": "🙆", + "old_key": "🗝", + "older_adult": "🧓", + "older_man": "👴", + "older_person": "🧓", + "older_woman": "👵", + "om_symbol": "🕉", + "on": "🔛", + "oncoming_automobile": "🚘", + "oncoming_bus": "🚍", + "oncoming_fist": "👊", + "oncoming_police_car": "🚔", + "oncoming_taxi": "🚖", + "one": "1⃣", + "onepiece_swimsuit": "🩱", + "onion": "🧅", + "open_file_folder": "📂", + "open_hands": "👐", + "open_mouth": "😮", + "ophiuchus": "⛎", + "orange_book": "📙", + "orange_circle": "🟠", + "orange_heart": "🧡", + "orange_square": "🟧", + "orangutan": "🦧", + "orthodox_cross": "☦️", + "otter": "🦦", + "outbox_tray": "📤", + "owl": "🦉", + "ox": "🐂", + "oyster": "🦪", + "p_button": "🅿", + "package": "📦", + "page_facing_up": "📄", + "page_with_curl": "📃", + "pager": "📟", + "paintbrush": "🖌", + "palm_tree": "🌴", + "palms_up_together": "🤲", + "pancakes": "🥞", + "panda_face": "🐼", + "paperclip": "📎", + "parachute": "🪂", + "parrot": "🦜", + "part_alternation_mark": "〽", + "partly_sunny": "⛅", + "partying_face": "🥳", + "passenger_ship": "🛳", + "passport_control": "🛂", + "pause_button": "⏸️", + "peace": "☮", + "peace_symbol": "☮️", + "peach": "🍑", + "peacock": "🦚", + "peanuts": "🥜", + "pear": "🍐", + "pen": "🖊", + "pencil": "📝", + "pencil2": "✏", + "penguin": "🐧", + "pensive": "😔", + "people_with_bunny_ears_partying": "👯", + "people_wrestling": "🤼", + "performing_arts": "🎭", + "persevere": "😣", + "person": "🧑", + "person_biking": "🚴", + "person_bouncing_ball": "⛹️", + "person_bowing": "🙇", + "person_cartwheeling": "🤸", + "person_climbing": "🧗", + "person_doing_cartwheel": "🤸", + "person_facepalming": "🤦", + "person_fencing": "🤺", + "person_frowning": "🙍", + "person_gesturing_no": "🙅", + "person_gesturing_ok": "🙆", + "person_getting_haircut": "💇", + "person_getting_massage": "💆", + "person_in_lotus_position": "🧘", + "person_in_steamy_room": "🧖", + "person_juggling": "🤹", + "person_kneeling": "🧎", + "person_mountain_biking": "🚵", + "person_playing_handball": "🤾", + "person_playing_water_polo": "🤽", + "person_pouting": "🙎", + "person_raising_hand": "🙋", + "person_rowing_boat": "🚣", + "person_running": "🏃", + "person_shrugging": "🤷", + "person_standing": "🧍", + "person_surfing": "🏄", + "person_swimming": "🏊", + "person_tipping_hand": "💁", + "person_walking": "🚶", + "person_wearing_turban": "👳", + "person_with_blond_hair": "👱", + "person_with_pouting_face": "🙎", + "petri_dish": "🧫", + "pick": "⛏️", + "pie": "🥧", + "pig": "🐷", + "pig2": "🐖", + "pig_nose": "🐽", + "pill": "💊", + "pinching_hand": "🤏", + "pineapple": "🍍", + "ping_pong": "🏓", + "pisces": "♓", + "pizza": "🍕", + "place_of_worship": "🛐", + "play_button": "▶", + "play_or_pause_button": "⏯️", + "play_pause": "⏯", + "pleading_face": "🥺", + "point_down": "👇", + "point_left": "👈", + "point_right": "👉", + "point_up": "☝️", + "point_up_2": "👆", + "police_car": "🚓", + "police_officer": "👮", + "poodle": "🐩", + "poop": "💩", + "popcorn": "🍿", + "post_office": "🏣", + "postal_horn": "📯", + "postbox": "📮", + "potable_water": "🚰", + "potato": "🥔", + "pouch": "👝", + "poultry_leg": "🍗", + "pound": "💷", + "pouting_cat": "😾", + "pray": "🙏", + "prayer_beads": "📿", + "pregnant_woman": "🤰", + "pretzel": "🥨", + "prince": "🤴", + "princess": "👸", + "printer": "🖨", + "probing_cane": "🦯", + "punch": "👊", + "purple_circle": "🟣", + "purple_heart": "💜", + "purse": "👛", + "pushpin": "📌", + "put_litter_in_its_place": "🚮", + "puzzle_piece": "🧩", + "question": "❓", + "rabbit": "🐰", + "rabbit2": "🐇", + "raccoon": "🦝", + "racehorse": "🐎", + "racing_car": "🏎", + "radio": "📻", + "radio_button": "🔘", + "radioactive": "☢️", + "rage": "😡", + "railway_car": "🚃", + "railway_track": "🛤", + "rainbow": "🌈", + "raised_back_of_hand": "🤚", + "raised_hand": "✋", + "raised_hands": "🙌", + "raising_hand": "🙋", + "ram": "🐏", + "ramen": "🍜", + "rat": "🐀", + "razor": "🪒", + "receipt": "🧾", + "record_button": "⏺️", + "recycle": "♻", + "recycling_symbol": "♻️", + "red_car": "🚗", + "red_circle": "🔴", + "red_envelope": "🧧", + "red_hair": "🦰", + "red_heart": "❤", + "red_square": "🟥", + "regional_indicator_a": "🇦", + "regional_indicator_b": "🇧", + "regional_indicator_c": "🇨", + "regional_indicator_d": "🇩", + "regional_indicator_e": "🇪", + "regional_indicator_f": "🇫", + "regional_indicator_g": "🇬", + "regional_indicator_h": "🇭", + "regional_indicator_i": "🇮", + "regional_indicator_j": "🇯", + "regional_indicator_k": "🇰", + "regional_indicator_l": "🇱", + "regional_indicator_m": "🇲", + "regional_indicator_n": "🇳", + "regional_indicator_o": "🇴", + "regional_indicator_p": "🇵", + "regional_indicator_q": "🇶", + "regional_indicator_r": "🇷", + "regional_indicator_s": "🇸", + "regional_indicator_t": "🇹", + "regional_indicator_u": "🇺", + "regional_indicator_v": "🇻", + "regional_indicator_w": "🇼", + "regional_indicator_x": "🇽", + "regional_indicator_y": "🇾", + "regional_indicator_z": "🇿", + "registered": "®", + "relieved": "😌", + "reminder_ribbon": "🎗", + "repeat": "🔁", + "repeat_one": "🔂", + "rescue_worker’s_helmet": "⛑️", + "restroom": "🚻", + "reverse_button": "◀", + "revolving_hearts": "💞", + "rewind": "⏪", + "rhino": "🦏", + "rhinoceros": "🦏", + "ribbon": "🎀", + "rice": "🍚", + "rice_ball": "🍙", + "rice_cracker": "🍘", + "rice_scene": "🎑", + "right_arrow": "➡️", + "right_arrow_curving_down": "⤵", + "right_arrow_curving_left": "↩", + "right_arrow_curving_up": "⤴", + "right_facing_fist": "🤜", + "rightfacing_fist": "🤜", + "ring": "💍", + "ringed_planet": "🪐", + "robot": "🤖", + "rocket": "🚀", + "rofl": "🤣", + "roll_of_paper": "🧻", + "rolledup_newspaper": "🗞", + "roller_coaster": "🎢", + "rolling_eyes": "🙄", + "rolling_on_the_floor_laughing": "🤣", + "rooster": "🐓", + "rose": "🌹", + "rosette": "🏵", + "rotating_light": "🚨", + "round_pushpin": "📍", + "rowboat": "🚣", + "rugby_football": "🏉", + "runner": "🏃", + "running_shirt_with_sash": "🎽", + "safety_pin": "🧷", + "safety_vest": "🦺", + "sagittarius": "♐", + "sailboat": "⛵", + "sake": "🍶", + "salad": "🥗", + "salt": "🧂", + "sandal": "👡", + "sandwich": "🥪", + "santa": "🎅", + "sari": "🥻", + "satellite": "📡", + "sauropod": "🦕", + "saxophone": "🎷", + "scales": "⚖", + "scarf": "🧣", + "school": "🏫", + "school_satchel": "🎒", + "scissors": "✂", + "scooter": "🛴", + "scorpion": "🦂", + "scorpius": "♏", + "scream": "😱", + "scream_cat": "🙀", + "scroll": "📜", + "seat": "💺", + "second_place": "🥈", + "secret": "㊙", + "see_no_evil": "🙈", + "seedling": "🌱", + "selfie": "🤳", + "seven": "7⃣", + "shallow_pan_of_food": "🥘", + "shamrock": "☘️", + "shark": "🦈", + "shaved_ice": "🍧", + "sheep": "🐑", + "shell": "🐚", + "shield": "🛡", + "shinto_shrine": "⛩️", + "ship": "🚢", + "shirt": "👕", + "shopping_bags": "🛍", + "shopping_cart": "🛒", + "shorts": "🩳", + "shower": "🚿", + "shrimp": "🦐", + "shushing_face": "🤫", + "sign_of_the_horns": "🤘", + "signal_strength": "📶", + "six": "6⃣", + "six_pointed_star": "🔯", + "skateboard": "🛹", + "ski": "🎿", + "skier": "⛷️", + "skull": "💀", + "skull_and_crossbones": "☠️", + "skull_crossbones": "☠", + "skunk": "🦨", + "sled": "🛷", + "sleeping": "😴", + "sleeping_accommodation": "🛌", + "sleepy": "😪", + "slight_frown": "🙁", + "slight_smile": "🙂", + "slightly_frowning_face": "🙁", + "slot_machine": "🎰", + "sloth": "🦥", + "small_airplane": "🛩", + "small_blue_diamond": "🔹", + "small_orange_diamond": "🔸", + "small_red_triangle": "🔺", + "small_red_triangle_down": "🔻", + "smile": "😄", + "smile_cat": "😸", + "smiley": "😃", + "smiley_cat": "😺", + "smiling": "☺️", + "smiling_face": "☺", + "smiling_face_with_hearts": "🥰", + "smiling_imp": "😈", + "smirk": "😏", + "smirk_cat": "😼", + "smoking": "🚬", + "snail": "🐌", + "snake": "🐍", + "sneezing_face": "🤧", + "snowboarder": "🏂", + "snowcapped_mountain": "🏔", + "snowflake": "❄", + "snowman": "⛄", + "soap": "🧼", + "sob": "😭", + "soccer": "⚽", + "socks": "🧦", + "softball": "🥎", + "soon": "🔜", + "sos": "🆘", + "sound": "🔉", + "space_invader": "👾", + "spade_suit": "♠️", + "spades": "♠", + "spaghetti": "🍝", + "sparkle": "❇", + "sparkler": "🎇", + "sparkles": "✨", + "sparkling_heart": "💖", + "speak_no_evil": "🙊", + "speaker": "🔈", + "speaking_head": "🗣", + "speech_balloon": "💬", + "speech_left": "🗨", + "speedboat": "🚤", + "spider": "🕷", + "spider_web": "🕸", + "spiral_calendar": "🗓", + "spiral_notepad": "🗒", + "sponge": "🧽", + "spoon": "🥄", + "squid": "🦑", + "stadium": "🏟", + "star": "⭐", + "star2": "🌟", + "star_and_crescent": "☪️", + "star_of_david": "✡", + "star_struck": "🤩", + "stars": "🌠", + "starstruck": "🤩", + "station": "🚉", + "statue_of_liberty": "🗽", + "steam_locomotive": "🚂", + "stethoscope": "🩺", + "stew": "🍲", + "stop_button": "⏹️", + "stopwatch": "⏱️", + "straight_ruler": "📏", + "strawberry": "🍓", + "stuck_out_tongue": "😛", + "stuck_out_tongue_closed_eyes": "😝", + "stuck_out_tongue_winking_eye": "😜", + "studio_microphone": "🎙", + "stuffed_flatbread": "🥙", + "sun": "☀", + "sun_behind_large_cloud": "🌥", + "sun_behind_rain_cloud": "🌦", + "sun_behind_small_cloud": "🌤", + "sun_with_face": "🌞", + "sunflower": "🌻", + "sunglasses": "😎", + "sunny": "☀️", + "sunrise": "🌅", + "sunrise_over_mountains": "🌄", + "superhero": "🦸", + "supervillain": "🦹", + "surfer": "🏄", + "sushi": "🍣", + "suspension_railway": "🚟", + "swan": "🦢", + "sweat": "😓", + "sweat_drops": "💦", + "sweat_smile": "😅", + "sweet_potato": "🍠", + "swimmer": "🏊", + "symbols": "🔣", + "synagogue": "🕍", + "syringe": "💉", + "t_rex": "🦖", + "taco": "🌮", + "tada": "🎉", + "takeout_box": "🥡", + "tanabata_tree": "🎋", + "tangerine": "🍊", + "taurus": "♉", + "taxi": "🚕", + "tea": "🍵", + "teddy_bear": "🧸", + "telephone": "☎", + "telephone_receiver": "📞", + "telescope": "🔭", + "tennis": "🎾", + "tent": "⛺", + "test_tube": "🧪", + "thermometer": "🌡", + "thermometer_face": "🤒", + "thinking": "🤔", + "third_place": "🥉", + "thought_balloon": "💭", + "thread": "🧵", + "three": "3⃣", + "thumbsdown": "👎", + "thumbsup": "👍", + "ticket": "🎫", + "tiger": "🐯", + "tiger2": "🐅", + "timer_clock": "⏲️", + "tired_face": "😫", + "tm": "™", + "toilet": "🚽", + "tokyo_tower": "🗼", + "tomato": "🍅", + "tone1": "🏻", + "tone2": "🏼", + "tone3": "🏽", + "tone4": "🏾", + "tone5": "🏿", + "tongue": "👅", + "toolbox": "🧰", + "tooth": "🦷", + "top": "🔝", + "tophat": "🎩", + "tornado": "🌪", + "track_next": "⏭", + "track_previous": "⏮", + "trackball": "🖲", + "tractor": "🚜", + "trade_mark": "™️", + "traffic_light": "🚥", + "train": "🚋", + "train2": "🚆", + "tram": "🚊", + "trex": "🦖", + "triangular_flag_on_post": "🚩", + "triangular_ruler": "📐", + "trident": "🔱", + "triumph": "😤", + "trolleybus": "🚎", + "trophy": "🏆", + "tropical_drink": "🍹", + "tropical_fish": "🐠", + "truck": "🚚", + "trumpet": "🎺", + "tulip": "🌷", + "tumbler_glass": "🥃", + "turkey": "🦃", + "turtle": "🐢", + "tv": "📺", + "twisted_rightwards_arrows": "🔀", + "two": "2⃣", + "two_hearts": "💕", + "two_men_holding_hands": "👬", + "two_women_holding_hands": "👭", + "u5272": "🈹", + "u5408": "🈴", + "u55b6": "🈺", + "u6307": "🈯", + "u6708": "🈷", + "u6709": "🈶", + "u6e80": "🈵", + "u7121": "🈚", + "u7533": "🈸", + "u7981": "🈲", + "u7a7a": "🈳", + "umbrella": "☔", + "umbrella_on_ground": "⛱️", + "unamused": "😒", + "underage": "🔞", + "unicorn": "🦄", + "unlock": "🔓", + "up": "🆙", + "up_arrow": "⬆", + "updown_arrow": "↕️", + "upleft_arrow": "↖️", + "upright_arrow": "↗", + "upside_down": "🙃", + "v": "✌️", + "vampire": "🧛", + "vertical_traffic_light": "🚦", + "vhs": "📼", + "vibration_mode": "📳", + "victory_hand": "✌", + "video_camera": "📹", + "video_game": "🎮", + "violin": "🎻", + "virgo": "♍", + "volcano": "🌋", + "volleyball": "🏐", + "vs": "🆚", + "vulcan": "🖖", + "vulcan_salute": "🖖", + "waffle": "🧇", + "walking": "🚶", + "waning_crescent_moon": "🌘", + "waning_gibbous_moon": "🌖", + "warning": "⚠", + "wastebasket": "🗑", + "watch": "⌚", + "water_buffalo": "🐃", + "watermelon": "🍉", + "wave": "👋", + "wavy_dash": "〰️", + "waxing_crescent_moon": "🌒", + "waxing_gibbous_moon": "🌔", + "wc": "🚾", + "weary": "😩", + "wedding": "💒", + "weightlifter": "🏋", + "whale": "🐳", + "whale2": "🐋", + "wheel_of_dharma": "☸️", + "wheelchair": "♿", + "white_check_mark": "✅", + "white_circle": "⚪", + "white_flower": "💮", + "white_hair": "🦳", + "white_heart": "🤍", + "white_large_square": "⬜", + "white_medium_small_square": "◽", + "white_medium_square": "◻️", + "white_small_square": "▫️", + "white_square_button": "🔳", + "wilted_flower": "🥀", + "wilted_rose": "🥀", + "wind_blowing_face": "🌬", + "wind_chime": "🎐", + "wine_glass": "🍷", + "wink": "😉", + "wolf": "🐺", + "woman": "👩", + "woman_with_headscarf": "🧕", + "womans_clothes": "👚", + "womans_hat": "👒", + "womens": "🚺", + "woozy_face": "🥴", + "world_map": "🗺", + "worried": "😟", + "wrench": "🔧", + "writing_hand": "✍️", + "x": "❌", + "yarn": "🧶", + "yawning_face": "🥱", + "yellow_circle": "🟡", + "yellow_heart": "💛", + "yellow_square": "🟨", + "yen": "💴", + "yin_yang": "☯️", + "yoyo": "🪀", + "yum": "😋", + "zany_face": "🤪", + "zap": "⚡", + "zebra": "🦓", + "zero": "0⃣", + "zipper_mouth": "🤐", + "zombie": "🧟", + "zzz": "💤" +} \ No newline at end of file diff --git a/priv/static/static/js/10.46f441b948010eda4403.js b/priv/static/static/js/10.46f441b948010eda4403.js deleted file mode 100644 index 308d124c0..000000000 Binary files a/priv/static/static/js/10.46f441b948010eda4403.js and /dev/null differ diff --git a/priv/static/static/js/10.9a138b362edd4833778a.js b/priv/static/static/js/10.9a138b362edd4833778a.js new file mode 100644 index 000000000..0518eec26 Binary files /dev/null and b/priv/static/static/js/10.9a138b362edd4833778a.js differ diff --git a/priv/static/static/js/10.46f441b948010eda4403.js.map b/priv/static/static/js/10.9a138b362edd4833778a.js.map similarity index 56% rename from priv/static/static/js/10.46f441b948010eda4403.js.map rename to priv/static/static/js/10.9a138b362edd4833778a.js.map index e0623e6bf..6a0c72332 100644 Binary files a/priv/static/static/js/10.46f441b948010eda4403.js.map and b/priv/static/static/js/10.9a138b362edd4833778a.js.map differ diff --git a/priv/static/static/js/11.8ff1ed54814f2d34cb3e.js b/priv/static/static/js/11.9d88e9e710c4e0d0c897.js similarity index 99% rename from priv/static/static/js/11.8ff1ed54814f2d34cb3e.js rename to priv/static/static/js/11.9d88e9e710c4e0d0c897.js index cb57f2a65..cbfd37408 100644 Binary files a/priv/static/static/js/11.8ff1ed54814f2d34cb3e.js and b/priv/static/static/js/11.9d88e9e710c4e0d0c897.js differ diff --git a/priv/static/static/js/11.8ff1ed54814f2d34cb3e.js.map b/priv/static/static/js/11.9d88e9e710c4e0d0c897.js.map similarity index 56% rename from priv/static/static/js/11.8ff1ed54814f2d34cb3e.js.map rename to priv/static/static/js/11.9d88e9e710c4e0d0c897.js.map index 4ce6d7227..ae6a2b9c4 100644 Binary files a/priv/static/static/js/11.8ff1ed54814f2d34cb3e.js.map and b/priv/static/static/js/11.9d88e9e710c4e0d0c897.js.map differ diff --git a/priv/static/static/js/12.13204bdd0ad5703a3ea3.js b/priv/static/static/js/12.36ee26c30e2909c8be60.js similarity index 99% rename from priv/static/static/js/12.13204bdd0ad5703a3ea3.js rename to priv/static/static/js/12.36ee26c30e2909c8be60.js index a89bfeb67..418717dfc 100644 Binary files a/priv/static/static/js/12.13204bdd0ad5703a3ea3.js and b/priv/static/static/js/12.36ee26c30e2909c8be60.js differ diff --git a/priv/static/static/js/12.13204bdd0ad5703a3ea3.js.map b/priv/static/static/js/12.36ee26c30e2909c8be60.js.map similarity index 56% rename from priv/static/static/js/12.13204bdd0ad5703a3ea3.js.map rename to priv/static/static/js/12.36ee26c30e2909c8be60.js.map index 366ec2927..1f9de123e 100644 Binary files a/priv/static/static/js/12.13204bdd0ad5703a3ea3.js.map and b/priv/static/static/js/12.36ee26c30e2909c8be60.js.map differ diff --git a/priv/static/static/js/13.e27c3eeddcc4b11c1f54.js b/priv/static/static/js/13.d02023d426b44a6886af.js similarity index 99% rename from priv/static/static/js/13.e27c3eeddcc4b11c1f54.js rename to priv/static/static/js/13.d02023d426b44a6886af.js index 8cd482b41..13f1abcb0 100644 Binary files a/priv/static/static/js/13.e27c3eeddcc4b11c1f54.js and b/priv/static/static/js/13.d02023d426b44a6886af.js differ diff --git a/priv/static/static/js/13.e27c3eeddcc4b11c1f54.js.map b/priv/static/static/js/13.d02023d426b44a6886af.js.map similarity index 56% rename from priv/static/static/js/13.e27c3eeddcc4b11c1f54.js.map rename to priv/static/static/js/13.d02023d426b44a6886af.js.map index 0c61c3fca..a202df0d2 100644 Binary files a/priv/static/static/js/13.e27c3eeddcc4b11c1f54.js.map and b/priv/static/static/js/13.d02023d426b44a6886af.js.map differ diff --git a/priv/static/static/js/14.273855b3e4e27ce80219.js b/priv/static/static/js/14.273855b3e4e27ce80219.js deleted file mode 100644 index 78c0bfebc..000000000 Binary files a/priv/static/static/js/14.273855b3e4e27ce80219.js and /dev/null differ diff --git a/priv/static/static/js/14.273855b3e4e27ce80219.js.map b/priv/static/static/js/14.273855b3e4e27ce80219.js.map deleted file mode 100644 index 9ee527eaa..000000000 Binary files a/priv/static/static/js/14.273855b3e4e27ce80219.js.map and /dev/null differ diff --git a/priv/static/static/js/14.8e7c0873d041b34efd84.js b/priv/static/static/js/14.8e7c0873d041b34efd84.js new file mode 100644 index 000000000..74432f43d Binary files /dev/null and b/priv/static/static/js/14.8e7c0873d041b34efd84.js differ diff --git a/priv/static/static/js/14.8e7c0873d041b34efd84.js.map b/priv/static/static/js/14.8e7c0873d041b34efd84.js.map new file mode 100644 index 000000000..e73fcfe38 Binary files /dev/null and b/priv/static/static/js/14.8e7c0873d041b34efd84.js.map differ diff --git a/priv/static/static/js/15.afbe29b6665fcd015b2d.js b/priv/static/static/js/15.349c342e2fe7e9e0fe01.js similarity index 98% rename from priv/static/static/js/15.afbe29b6665fcd015b2d.js rename to priv/static/static/js/15.349c342e2fe7e9e0fe01.js index b83752240..f77855cfc 100644 Binary files a/priv/static/static/js/15.afbe29b6665fcd015b2d.js and b/priv/static/static/js/15.349c342e2fe7e9e0fe01.js differ diff --git a/priv/static/static/js/15.349c342e2fe7e9e0fe01.js.map b/priv/static/static/js/15.349c342e2fe7e9e0fe01.js.map new file mode 100644 index 000000000..d9394d405 Binary files /dev/null and b/priv/static/static/js/15.349c342e2fe7e9e0fe01.js.map differ diff --git a/priv/static/static/js/15.afbe29b6665fcd015b2d.js.map b/priv/static/static/js/15.afbe29b6665fcd015b2d.js.map deleted file mode 100644 index c7a0be582..000000000 Binary files a/priv/static/static/js/15.afbe29b6665fcd015b2d.js.map and /dev/null differ diff --git a/priv/static/static/js/16.5e3f20da470591d0cabf.js.map b/priv/static/static/js/16.5e3f20da470591d0cabf.js.map deleted file mode 100644 index 0c4d0e385..000000000 Binary files a/priv/static/static/js/16.5e3f20da470591d0cabf.js.map and /dev/null differ diff --git a/priv/static/static/js/16.5e3f20da470591d0cabf.js b/priv/static/static/js/16.cf210505237c2a0040dd.js similarity index 98% rename from priv/static/static/js/16.5e3f20da470591d0cabf.js rename to priv/static/static/js/16.cf210505237c2a0040dd.js index e90ed4ca1..4341c4e6e 100644 Binary files a/priv/static/static/js/16.5e3f20da470591d0cabf.js and b/priv/static/static/js/16.cf210505237c2a0040dd.js differ diff --git a/priv/static/static/js/16.cf210505237c2a0040dd.js.map b/priv/static/static/js/16.cf210505237c2a0040dd.js.map new file mode 100644 index 000000000..e6c3a7b56 Binary files /dev/null and b/priv/static/static/js/16.cf210505237c2a0040dd.js.map differ diff --git a/priv/static/static/js/17.44e90ef82ee2ef12dc3f.js.map b/priv/static/static/js/17.44e90ef82ee2ef12dc3f.js.map deleted file mode 100644 index 1d191b94a..000000000 Binary files a/priv/static/static/js/17.44e90ef82ee2ef12dc3f.js.map and /dev/null differ diff --git a/priv/static/static/js/17.44e90ef82ee2ef12dc3f.js b/priv/static/static/js/17.fe52ece512025177a3b8.js similarity index 94% rename from priv/static/static/js/17.44e90ef82ee2ef12dc3f.js rename to priv/static/static/js/17.fe52ece512025177a3b8.js index 9b5adfd12..25d7ed77c 100644 Binary files a/priv/static/static/js/17.44e90ef82ee2ef12dc3f.js and b/priv/static/static/js/17.fe52ece512025177a3b8.js differ diff --git a/priv/static/static/js/17.fe52ece512025177a3b8.js.map b/priv/static/static/js/17.fe52ece512025177a3b8.js.map new file mode 100644 index 000000000..cfdd5717d Binary files /dev/null and b/priv/static/static/js/17.fe52ece512025177a3b8.js.map differ diff --git a/priv/static/static/js/18.9a5b877f94b2b53065e1.js b/priv/static/static/js/18.9a5b877f94b2b53065e1.js deleted file mode 100644 index c4aea5b25..000000000 Binary files a/priv/static/static/js/18.9a5b877f94b2b53065e1.js and /dev/null differ diff --git a/priv/static/static/js/18.9a5b877f94b2b53065e1.js.map b/priv/static/static/js/18.9a5b877f94b2b53065e1.js.map deleted file mode 100644 index 61d9a7d41..000000000 Binary files a/priv/static/static/js/18.9a5b877f94b2b53065e1.js.map and /dev/null differ diff --git a/priv/static/static/js/18.c0d447ff77030482a94c.js b/priv/static/static/js/18.c0d447ff77030482a94c.js new file mode 100644 index 000000000..4bb119120 Binary files /dev/null and b/priv/static/static/js/18.c0d447ff77030482a94c.js differ diff --git a/priv/static/static/js/18.c0d447ff77030482a94c.js.map b/priv/static/static/js/18.c0d447ff77030482a94c.js.map new file mode 100644 index 000000000..d2ff3aa95 Binary files /dev/null and b/priv/static/static/js/18.c0d447ff77030482a94c.js.map differ diff --git a/priv/static/static/js/19.1fd4da643df0abf89122.js.map b/priv/static/static/js/19.1fd4da643df0abf89122.js.map deleted file mode 100644 index 010c8674d..000000000 Binary files a/priv/static/static/js/19.1fd4da643df0abf89122.js.map and /dev/null differ diff --git a/priv/static/static/js/19.1fd4da643df0abf89122.js b/priv/static/static/js/19.9b0c21b72dedc1b244bd.js similarity index 99% rename from priv/static/static/js/19.1fd4da643df0abf89122.js rename to priv/static/static/js/19.9b0c21b72dedc1b244bd.js index c1ca1643b..0c8605d10 100644 Binary files a/priv/static/static/js/19.1fd4da643df0abf89122.js and b/priv/static/static/js/19.9b0c21b72dedc1b244bd.js differ diff --git a/priv/static/static/js/19.9b0c21b72dedc1b244bd.js.map b/priv/static/static/js/19.9b0c21b72dedc1b244bd.js.map new file mode 100644 index 000000000..8f3305b3a Binary files /dev/null and b/priv/static/static/js/19.9b0c21b72dedc1b244bd.js.map differ diff --git a/priv/static/static/js/2.422e6c756ac673a6fd44.js b/priv/static/static/js/2.422e6c756ac673a6fd44.js deleted file mode 100644 index 9fb47e2bf..000000000 Binary files a/priv/static/static/js/2.422e6c756ac673a6fd44.js and /dev/null differ diff --git a/priv/static/static/js/2.422e6c756ac673a6fd44.js.map b/priv/static/static/js/2.422e6c756ac673a6fd44.js.map deleted file mode 100644 index 92fdb4d2c..000000000 Binary files a/priv/static/static/js/2.422e6c756ac673a6fd44.js.map and /dev/null differ diff --git a/priv/static/static/js/2.80ae75b951121aacd208.js b/priv/static/static/js/2.80ae75b951121aacd208.js new file mode 100644 index 000000000..e2e37bf88 Binary files /dev/null and b/priv/static/static/js/2.80ae75b951121aacd208.js differ diff --git a/priv/static/static/js/2.80ae75b951121aacd208.js.map b/priv/static/static/js/2.80ae75b951121aacd208.js.map new file mode 100644 index 000000000..5528b7116 Binary files /dev/null and b/priv/static/static/js/2.80ae75b951121aacd208.js.map differ diff --git a/priv/static/static/js/20.a64fd29da59076399a27.js b/priv/static/static/js/20.a64fd29da59076399a27.js deleted file mode 100644 index eae5b3947..000000000 Binary files a/priv/static/static/js/20.a64fd29da59076399a27.js and /dev/null differ diff --git a/priv/static/static/js/20.a64fd29da59076399a27.js.map b/priv/static/static/js/20.a64fd29da59076399a27.js.map deleted file mode 100644 index b2917fa10..000000000 Binary files a/priv/static/static/js/20.a64fd29da59076399a27.js.map and /dev/null differ diff --git a/priv/static/static/js/20.fee3cd69d629f271e653.js b/priv/static/static/js/20.fee3cd69d629f271e653.js new file mode 100644 index 000000000..8dc617e25 Binary files /dev/null and b/priv/static/static/js/20.fee3cd69d629f271e653.js differ diff --git a/priv/static/static/js/20.fee3cd69d629f271e653.js.map b/priv/static/static/js/20.fee3cd69d629f271e653.js.map new file mode 100644 index 000000000..b80afa931 Binary files /dev/null and b/priv/static/static/js/20.fee3cd69d629f271e653.js.map differ diff --git a/priv/static/static/js/21.243d9e6ebf469a2dc740.js b/priv/static/static/js/21.243d9e6ebf469a2dc740.js deleted file mode 100644 index 61633519b..000000000 Binary files a/priv/static/static/js/21.243d9e6ebf469a2dc740.js and /dev/null differ diff --git a/priv/static/static/js/21.243d9e6ebf469a2dc740.js.map b/priv/static/static/js/21.243d9e6ebf469a2dc740.js.map deleted file mode 100644 index 3f98250fa..000000000 Binary files a/priv/static/static/js/21.243d9e6ebf469a2dc740.js.map and /dev/null differ diff --git a/priv/static/static/js/21.9b5434a9d2b0b07a3038.js b/priv/static/static/js/21.9b5434a9d2b0b07a3038.js new file mode 100644 index 000000000..e01b51a44 Binary files /dev/null and b/priv/static/static/js/21.9b5434a9d2b0b07a3038.js differ diff --git a/priv/static/static/js/21.9b5434a9d2b0b07a3038.js.map b/priv/static/static/js/21.9b5434a9d2b0b07a3038.js.map new file mode 100644 index 000000000..cb2792ac9 Binary files /dev/null and b/priv/static/static/js/21.9b5434a9d2b0b07a3038.js.map differ diff --git a/priv/static/static/js/22.132b7fba6bee9817b39f.js b/priv/static/static/js/22.132b7fba6bee9817b39f.js new file mode 100644 index 000000000..a0f30bc81 Binary files /dev/null and b/priv/static/static/js/22.132b7fba6bee9817b39f.js differ diff --git a/priv/static/static/js/22.132b7fba6bee9817b39f.js.map b/priv/static/static/js/22.132b7fba6bee9817b39f.js.map new file mode 100644 index 000000000..5b54e2be5 Binary files /dev/null and b/priv/static/static/js/22.132b7fba6bee9817b39f.js.map differ diff --git a/priv/static/static/js/22.e20ef7e5fefc0964cdd1.js b/priv/static/static/js/22.e20ef7e5fefc0964cdd1.js deleted file mode 100644 index e8f309f8a..000000000 Binary files a/priv/static/static/js/22.e20ef7e5fefc0964cdd1.js and /dev/null differ diff --git a/priv/static/static/js/22.e20ef7e5fefc0964cdd1.js.map b/priv/static/static/js/22.e20ef7e5fefc0964cdd1.js.map deleted file mode 100644 index 7780cffe6..000000000 Binary files a/priv/static/static/js/22.e20ef7e5fefc0964cdd1.js.map and /dev/null differ diff --git a/priv/static/static/js/23.614a35f9ded445292f4a.js.map b/priv/static/static/js/23.614a35f9ded445292f4a.js.map deleted file mode 100644 index 4158041f4..000000000 Binary files a/priv/static/static/js/23.614a35f9ded445292f4a.js.map and /dev/null differ diff --git a/priv/static/static/js/23.614a35f9ded445292f4a.js b/priv/static/static/js/23.63b95894e9f0eb300da0.js similarity index 99% rename from priv/static/static/js/23.614a35f9ded445292f4a.js rename to priv/static/static/js/23.63b95894e9f0eb300da0.js index a35450986..aeef30063 100644 Binary files a/priv/static/static/js/23.614a35f9ded445292f4a.js and b/priv/static/static/js/23.63b95894e9f0eb300da0.js differ diff --git a/priv/static/static/js/23.63b95894e9f0eb300da0.js.map b/priv/static/static/js/23.63b95894e9f0eb300da0.js.map new file mode 100644 index 000000000..dbd9ba11e Binary files /dev/null and b/priv/static/static/js/23.63b95894e9f0eb300da0.js.map differ diff --git a/priv/static/static/js/24.6ae9ca51e51e023afbe4.js b/priv/static/static/js/24.10dc1eadca8b0bc15e20.js similarity index 99% rename from priv/static/static/js/24.6ae9ca51e51e023afbe4.js rename to priv/static/static/js/24.10dc1eadca8b0bc15e20.js index d075f3b1f..ed3bab7cc 100644 Binary files a/priv/static/static/js/24.6ae9ca51e51e023afbe4.js and b/priv/static/static/js/24.10dc1eadca8b0bc15e20.js differ diff --git a/priv/static/static/js/24.10dc1eadca8b0bc15e20.js.map b/priv/static/static/js/24.10dc1eadca8b0bc15e20.js.map new file mode 100644 index 000000000..82709d683 Binary files /dev/null and b/priv/static/static/js/24.10dc1eadca8b0bc15e20.js.map differ diff --git a/priv/static/static/js/24.6ae9ca51e51e023afbe4.js.map b/priv/static/static/js/24.6ae9ca51e51e023afbe4.js.map deleted file mode 100644 index 7e68d5eaa..000000000 Binary files a/priv/static/static/js/24.6ae9ca51e51e023afbe4.js.map and /dev/null differ diff --git a/priv/static/static/js/25.e2e834e1b024960e0087.js b/priv/static/static/js/25.e2e834e1b024960e0087.js new file mode 100644 index 000000000..c2caf0d62 Binary files /dev/null and b/priv/static/static/js/25.e2e834e1b024960e0087.js differ diff --git a/priv/static/static/js/25.e2e834e1b024960e0087.js.map b/priv/static/static/js/25.e2e834e1b024960e0087.js.map new file mode 100644 index 000000000..e4967e625 Binary files /dev/null and b/priv/static/static/js/25.e2e834e1b024960e0087.js.map differ diff --git a/priv/static/static/js/25.eadae0d48ee5be52a16c.js b/priv/static/static/js/25.eadae0d48ee5be52a16c.js deleted file mode 100644 index a0e44e1aa..000000000 Binary files a/priv/static/static/js/25.eadae0d48ee5be52a16c.js and /dev/null differ diff --git a/priv/static/static/js/25.eadae0d48ee5be52a16c.js.map b/priv/static/static/js/25.eadae0d48ee5be52a16c.js.map deleted file mode 100644 index aaa5e3a57..000000000 Binary files a/priv/static/static/js/25.eadae0d48ee5be52a16c.js.map and /dev/null differ diff --git a/priv/static/static/js/26.74667f919f7bad826ea0.js b/priv/static/static/js/26.74667f919f7bad826ea0.js new file mode 100644 index 000000000..712c57182 Binary files /dev/null and b/priv/static/static/js/26.74667f919f7bad826ea0.js differ diff --git a/priv/static/static/js/26.74667f919f7bad826ea0.js.map b/priv/static/static/js/26.74667f919f7bad826ea0.js.map new file mode 100644 index 000000000..43a64a1fb Binary files /dev/null and b/priv/static/static/js/26.74667f919f7bad826ea0.js.map differ diff --git a/priv/static/static/js/26.8fd0027b982c4bcdc88f.js b/priv/static/static/js/26.8fd0027b982c4bcdc88f.js deleted file mode 100644 index 3b149915b..000000000 Binary files a/priv/static/static/js/26.8fd0027b982c4bcdc88f.js and /dev/null differ diff --git a/priv/static/static/js/26.8fd0027b982c4bcdc88f.js.map b/priv/static/static/js/26.8fd0027b982c4bcdc88f.js.map deleted file mode 100644 index d40f1979a..000000000 Binary files a/priv/static/static/js/26.8fd0027b982c4bcdc88f.js.map and /dev/null differ diff --git a/priv/static/static/js/27.6d90a54efba08d261d69.js b/priv/static/static/js/27.0af03540f78df63eddca.js similarity index 94% rename from priv/static/static/js/27.6d90a54efba08d261d69.js rename to priv/static/static/js/27.0af03540f78df63eddca.js index e8420a54f..86d8c0045 100644 Binary files a/priv/static/static/js/27.6d90a54efba08d261d69.js and b/priv/static/static/js/27.0af03540f78df63eddca.js differ diff --git a/priv/static/static/js/27.0af03540f78df63eddca.js.map b/priv/static/static/js/27.0af03540f78df63eddca.js.map new file mode 100644 index 000000000..a1e846519 Binary files /dev/null and b/priv/static/static/js/27.0af03540f78df63eddca.js.map differ diff --git a/priv/static/static/js/27.6d90a54efba08d261d69.js.map b/priv/static/static/js/27.6d90a54efba08d261d69.js.map deleted file mode 100644 index 6685474ce..000000000 Binary files a/priv/static/static/js/27.6d90a54efba08d261d69.js.map and /dev/null differ diff --git a/priv/static/static/js/28.599a889517f15c01b27e.js b/priv/static/static/js/28.599a889517f15c01b27e.js new file mode 100644 index 000000000..6f02d5cf6 Binary files /dev/null and b/priv/static/static/js/28.599a889517f15c01b27e.js differ diff --git a/priv/static/static/js/28.599a889517f15c01b27e.js.map b/priv/static/static/js/28.599a889517f15c01b27e.js.map new file mode 100644 index 000000000..d12cd5dae Binary files /dev/null and b/priv/static/static/js/28.599a889517f15c01b27e.js.map differ diff --git a/priv/static/static/js/28.f1353aa382a104262d1a.js b/priv/static/static/js/28.f1353aa382a104262d1a.js deleted file mode 100644 index a253284f0..000000000 Binary files a/priv/static/static/js/28.f1353aa382a104262d1a.js and /dev/null differ diff --git a/priv/static/static/js/28.f1353aa382a104262d1a.js.map b/priv/static/static/js/28.f1353aa382a104262d1a.js.map deleted file mode 100644 index 3421c9511..000000000 Binary files a/priv/static/static/js/28.f1353aa382a104262d1a.js.map and /dev/null differ diff --git a/priv/static/static/js/29.39c1e87a689c840395b2.js.map b/priv/static/static/js/29.39c1e87a689c840395b2.js.map deleted file mode 100644 index 5901ce9b7..000000000 Binary files a/priv/static/static/js/29.39c1e87a689c840395b2.js.map and /dev/null differ diff --git a/priv/static/static/js/29.39c1e87a689c840395b2.js b/priv/static/static/js/29.3fc5f707254d05a94c4e.js similarity index 99% rename from priv/static/static/js/29.39c1e87a689c840395b2.js rename to priv/static/static/js/29.3fc5f707254d05a94c4e.js index ddb512279..fbb53794e 100644 Binary files a/priv/static/static/js/29.39c1e87a689c840395b2.js and b/priv/static/static/js/29.3fc5f707254d05a94c4e.js differ diff --git a/priv/static/static/js/29.3fc5f707254d05a94c4e.js.map b/priv/static/static/js/29.3fc5f707254d05a94c4e.js.map new file mode 100644 index 000000000..d9dc3432e Binary files /dev/null and b/priv/static/static/js/29.3fc5f707254d05a94c4e.js.map differ diff --git a/priv/static/static/js/3.a0df8a5bcd120d1f8581.js b/priv/static/static/js/3.716f85efb43de512faf0.js similarity index 99% rename from priv/static/static/js/3.a0df8a5bcd120d1f8581.js rename to priv/static/static/js/3.716f85efb43de512faf0.js index 423121114..c62e430f2 100644 Binary files a/priv/static/static/js/3.a0df8a5bcd120d1f8581.js and b/priv/static/static/js/3.716f85efb43de512faf0.js differ diff --git a/priv/static/static/js/3.716f85efb43de512faf0.js.map b/priv/static/static/js/3.716f85efb43de512faf0.js.map new file mode 100644 index 000000000..877f25345 Binary files /dev/null and b/priv/static/static/js/3.716f85efb43de512faf0.js.map differ diff --git a/priv/static/static/js/3.a0df8a5bcd120d1f8581.js.map b/priv/static/static/js/3.a0df8a5bcd120d1f8581.js.map deleted file mode 100644 index 653727d10..000000000 Binary files a/priv/static/static/js/3.a0df8a5bcd120d1f8581.js.map and /dev/null differ diff --git a/priv/static/static/js/30.64736585965c63c2b5d4.js b/priv/static/static/js/30.64736585965c63c2b5d4.js deleted file mode 100644 index 4fdbe8c3e..000000000 Binary files a/priv/static/static/js/30.64736585965c63c2b5d4.js and /dev/null differ diff --git a/priv/static/static/js/30.64736585965c63c2b5d4.js.map b/priv/static/static/js/30.64736585965c63c2b5d4.js.map deleted file mode 100644 index 376920946..000000000 Binary files a/priv/static/static/js/30.64736585965c63c2b5d4.js.map and /dev/null differ diff --git a/priv/static/static/js/30.af9dba19236c2e02ceb0.js b/priv/static/static/js/30.af9dba19236c2e02ceb0.js new file mode 100644 index 000000000..dda2ec2cb Binary files /dev/null and b/priv/static/static/js/30.af9dba19236c2e02ceb0.js differ diff --git a/priv/static/static/js/30.af9dba19236c2e02ceb0.js.map b/priv/static/static/js/30.af9dba19236c2e02ceb0.js.map new file mode 100644 index 000000000..e3094bf26 Binary files /dev/null and b/priv/static/static/js/30.af9dba19236c2e02ceb0.js.map differ diff --git a/priv/static/static/js/31.f4fb830b17ba4aa43cb0.js b/priv/static/static/js/31.f4fb830b17ba4aa43cb0.js new file mode 100644 index 000000000..65edaa3dd Binary files /dev/null and b/priv/static/static/js/31.f4fb830b17ba4aa43cb0.js differ diff --git a/priv/static/static/js/31.f4fb830b17ba4aa43cb0.js.map b/priv/static/static/js/31.f4fb830b17ba4aa43cb0.js.map new file mode 100644 index 000000000..4157d56ad Binary files /dev/null and b/priv/static/static/js/31.f4fb830b17ba4aa43cb0.js.map differ diff --git a/priv/static/static/js/32.e0c1e549e0806ed8c97e.js b/priv/static/static/js/32.e0c1e549e0806ed8c97e.js new file mode 100644 index 000000000..12e61f91a Binary files /dev/null and b/priv/static/static/js/32.e0c1e549e0806ed8c97e.js differ diff --git a/priv/static/static/js/32.e0c1e549e0806ed8c97e.js.map b/priv/static/static/js/32.e0c1e549e0806ed8c97e.js.map new file mode 100644 index 000000000..a30317255 Binary files /dev/null and b/priv/static/static/js/32.e0c1e549e0806ed8c97e.js.map differ diff --git a/priv/static/static/js/4.4cde7fdd1fe6bf2a9499.js b/priv/static/static/js/4.ae27cb41b81c1d0fb12b.js similarity index 80% rename from priv/static/static/js/4.4cde7fdd1fe6bf2a9499.js rename to priv/static/static/js/4.ae27cb41b81c1d0fb12b.js index 4da4c56fa..36db0a49a 100644 Binary files a/priv/static/static/js/4.4cde7fdd1fe6bf2a9499.js and b/priv/static/static/js/4.ae27cb41b81c1d0fb12b.js differ diff --git a/priv/static/static/js/4.4cde7fdd1fe6bf2a9499.js.map b/priv/static/static/js/4.ae27cb41b81c1d0fb12b.js.map similarity index 99% rename from priv/static/static/js/4.4cde7fdd1fe6bf2a9499.js.map rename to priv/static/static/js/4.ae27cb41b81c1d0fb12b.js.map index bc040ab9b..2885b8635 100644 Binary files a/priv/static/static/js/4.4cde7fdd1fe6bf2a9499.js.map and b/priv/static/static/js/4.ae27cb41b81c1d0fb12b.js.map differ diff --git a/priv/static/static/js/5.2e165bc072548e533dd4.js b/priv/static/static/js/5.730f6dd69f3216d24320.js similarity index 98% rename from priv/static/static/js/5.2e165bc072548e533dd4.js rename to priv/static/static/js/5.730f6dd69f3216d24320.js index cfd84226c..b9f9e7ce8 100644 Binary files a/priv/static/static/js/5.2e165bc072548e533dd4.js and b/priv/static/static/js/5.730f6dd69f3216d24320.js differ diff --git a/priv/static/static/js/5.2e165bc072548e533dd4.js.map b/priv/static/static/js/5.730f6dd69f3216d24320.js.map similarity index 57% rename from priv/static/static/js/5.2e165bc072548e533dd4.js.map rename to priv/static/static/js/5.730f6dd69f3216d24320.js.map index 49959c78e..f6e5d1301 100644 Binary files a/priv/static/static/js/5.2e165bc072548e533dd4.js.map and b/priv/static/static/js/5.730f6dd69f3216d24320.js.map differ diff --git a/priv/static/static/js/6.260ccd84f8cd2af27970.js b/priv/static/static/js/6.17d3d7ef67c646d7d9e2.js similarity index 99% rename from priv/static/static/js/6.260ccd84f8cd2af27970.js rename to priv/static/static/js/6.17d3d7ef67c646d7d9e2.js index fb4a690f4..7c37a2617 100644 Binary files a/priv/static/static/js/6.260ccd84f8cd2af27970.js and b/priv/static/static/js/6.17d3d7ef67c646d7d9e2.js differ diff --git a/priv/static/static/js/6.260ccd84f8cd2af27970.js.map b/priv/static/static/js/6.17d3d7ef67c646d7d9e2.js.map similarity index 57% rename from priv/static/static/js/6.260ccd84f8cd2af27970.js.map rename to priv/static/static/js/6.17d3d7ef67c646d7d9e2.js.map index 850fe731a..a86efcd83 100644 Binary files a/priv/static/static/js/6.260ccd84f8cd2af27970.js.map and b/priv/static/static/js/6.17d3d7ef67c646d7d9e2.js.map differ diff --git a/priv/static/static/js/7.1c41eff6cfc75a00bde4.js b/priv/static/static/js/7.c7fec9856bb057372873.js similarity index 99% rename from priv/static/static/js/7.1c41eff6cfc75a00bde4.js rename to priv/static/static/js/7.c7fec9856bb057372873.js index 317770a53..3f05de438 100644 Binary files a/priv/static/static/js/7.1c41eff6cfc75a00bde4.js and b/priv/static/static/js/7.c7fec9856bb057372873.js differ diff --git a/priv/static/static/js/7.1c41eff6cfc75a00bde4.js.map b/priv/static/static/js/7.c7fec9856bb057372873.js.map similarity index 57% rename from priv/static/static/js/7.1c41eff6cfc75a00bde4.js.map rename to priv/static/static/js/7.c7fec9856bb057372873.js.map index 36f327b3f..953c23427 100644 Binary files a/priv/static/static/js/7.1c41eff6cfc75a00bde4.js.map and b/priv/static/static/js/7.c7fec9856bb057372873.js.map differ diff --git a/priv/static/static/js/8.9b35c2fee24ab7481e00.js b/priv/static/static/js/8.c3a32861cd869f7892e5.js similarity index 99% rename from priv/static/static/js/8.9b35c2fee24ab7481e00.js rename to priv/static/static/js/8.c3a32861cd869f7892e5.js index cb7844ffc..1165de321 100644 Binary files a/priv/static/static/js/8.9b35c2fee24ab7481e00.js and b/priv/static/static/js/8.c3a32861cd869f7892e5.js differ diff --git a/priv/static/static/js/8.9b35c2fee24ab7481e00.js.map b/priv/static/static/js/8.c3a32861cd869f7892e5.js.map similarity index 57% rename from priv/static/static/js/8.9b35c2fee24ab7481e00.js.map rename to priv/static/static/js/8.c3a32861cd869f7892e5.js.map index 65f4d5ae9..1ae964e3d 100644 Binary files a/priv/static/static/js/8.9b35c2fee24ab7481e00.js.map and b/priv/static/static/js/8.c3a32861cd869f7892e5.js.map differ diff --git a/priv/static/static/js/9.2d36a607172a0f72bb59.js b/priv/static/static/js/9.2d36a607172a0f72bb59.js new file mode 100644 index 000000000..def839c6e Binary files /dev/null and b/priv/static/static/js/9.2d36a607172a0f72bb59.js differ diff --git a/priv/static/static/js/9.2d36a607172a0f72bb59.js.map b/priv/static/static/js/9.2d36a607172a0f72bb59.js.map new file mode 100644 index 000000000..d7ee040c4 Binary files /dev/null and b/priv/static/static/js/9.2d36a607172a0f72bb59.js.map differ diff --git a/priv/static/static/js/9.3a29094f1886648a0af3.js b/priv/static/static/js/9.3a29094f1886648a0af3.js deleted file mode 100644 index eb3516dcd..000000000 Binary files a/priv/static/static/js/9.3a29094f1886648a0af3.js and /dev/null differ diff --git a/priv/static/static/js/9.3a29094f1886648a0af3.js.map b/priv/static/static/js/9.3a29094f1886648a0af3.js.map deleted file mode 100644 index 1b6224a6a..000000000 Binary files a/priv/static/static/js/9.3a29094f1886648a0af3.js.map and /dev/null differ diff --git a/priv/static/static/js/app.2e518bb8cf82de7a72b0.js b/priv/static/static/js/app.2e518bb8cf82de7a72b0.js new file mode 100644 index 000000000..f610404a0 Binary files /dev/null and b/priv/static/static/js/app.2e518bb8cf82de7a72b0.js differ diff --git a/priv/static/static/js/app.2e518bb8cf82de7a72b0.js.map b/priv/static/static/js/app.2e518bb8cf82de7a72b0.js.map new file mode 100644 index 000000000..b84a6e676 Binary files /dev/null and b/priv/static/static/js/app.2e518bb8cf82de7a72b0.js.map differ diff --git a/priv/static/static/js/app.45547c05212c403dd77c.js b/priv/static/static/js/app.45547c05212c403dd77c.js deleted file mode 100644 index 219a59493..000000000 Binary files a/priv/static/static/js/app.45547c05212c403dd77c.js and /dev/null differ diff --git a/priv/static/static/js/app.45547c05212c403dd77c.js.map b/priv/static/static/js/app.45547c05212c403dd77c.js.map deleted file mode 100644 index e1dd6c992..000000000 Binary files a/priv/static/static/js/app.45547c05212c403dd77c.js.map and /dev/null differ diff --git a/priv/static/static/js/vendors~app.952124344a84613dbac0.js b/priv/static/static/js/vendors~app.102a26590d3ba87dd908.js similarity index 57% rename from priv/static/static/js/vendors~app.952124344a84613dbac0.js rename to priv/static/static/js/vendors~app.102a26590d3ba87dd908.js index f7943c122..d44c7f76b 100644 Binary files a/priv/static/static/js/vendors~app.952124344a84613dbac0.js and b/priv/static/static/js/vendors~app.102a26590d3ba87dd908.js differ diff --git a/priv/static/static/js/vendors~app.102a26590d3ba87dd908.js.map b/priv/static/static/js/vendors~app.102a26590d3ba87dd908.js.map new file mode 100644 index 000000000..82a20126a Binary files /dev/null and b/priv/static/static/js/vendors~app.102a26590d3ba87dd908.js.map differ diff --git a/priv/static/static/js/vendors~app.952124344a84613dbac0.js.map b/priv/static/static/js/vendors~app.952124344a84613dbac0.js.map deleted file mode 100644 index 05fc07c18..000000000 Binary files a/priv/static/static/js/vendors~app.952124344a84613dbac0.js.map and /dev/null differ diff --git a/priv/static/static/themes/redmond-xx-se.json b/priv/static/static/themes/redmond-xx-se.json index 24480d2c7..b62769dbc 100644 --- a/priv/static/static/themes/redmond-xx-se.json +++ b/priv/static/static/themes/redmond-xx-se.json @@ -267,6 +267,7 @@ }, "colors": { "bg": "#c0c0c0", + "wallpaper": "#008080", "text": "#000000", "link": "#0000ff", "accent": "#000080", diff --git a/priv/static/static/themes/redmond-xx.json b/priv/static/static/themes/redmond-xx.json index cf9010fe2..83b591091 100644 --- a/priv/static/static/themes/redmond-xx.json +++ b/priv/static/static/themes/redmond-xx.json @@ -258,6 +258,7 @@ }, "colors": { "bg": "#c0c0c0", + "wallpaper": "#008080", "text": "#000000", "link": "#0000ff", "accent": "#000080", diff --git a/priv/static/static/themes/redmond-xxi.json b/priv/static/static/themes/redmond-xxi.json index 7fdc4a6d6..60ceae7c2 100644 --- a/priv/static/static/themes/redmond-xxi.json +++ b/priv/static/static/themes/redmond-xxi.json @@ -240,6 +240,7 @@ }, "colors": { "bg": "#d6d6ce", + "wallpaper": "#396ba5", "text": "#000000", "link": "#0000ff", "accent": "#0a246a", diff --git a/priv/static/sw-pleroma.js b/priv/static/sw-pleroma.js index 385ee2f0c..e42440eb6 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 0b6a76c2f..d4c2345e0 100644 Binary files a/priv/static/sw-pleroma.js.map and b/priv/static/sw-pleroma.js.map differ diff --git a/priv/templates/sample_config.eex b/priv/templates/sample_config.eex index cdddc47ea..42f496ded 100644 --- a/priv/templates/sample_config.eex +++ b/priv/templates/sample_config.eex @@ -32,8 +32,7 @@ config :pleroma, Pleroma.Repo, username: "<%= dbuser %>", password: "<%= dbpass %>", database: "<%= dbname %>", - hostname: "<%= dbhost %>", - pool_size: 10 + hostname: "<%= dbhost %>" # Configure web push notifications config :web_push_encryption, :vapid_details, @@ -50,12 +49,18 @@ config :pleroma, Pleroma.Uploaders.Local, uploads: "<%= uploads_dir %>" # sts: true # Configure S3 support if desired. -# The public S3 endpoint is different depending on region and provider, +# The public S3 endpoint (base_url) is different depending on region and provider, # consult your S3 provider's documentation for details on what to use. # +# config :pleroma, Pleroma.Upload, +# uploader: Pleroma.Uploaders.S3, +# base_url: "https://s3.amazonaws.com" +# # config :pleroma, Pleroma.Uploaders.S3, # bucket: "some-bucket", -# public_endpoint: "https://s3.amazonaws.com" +# bucket_namespace: "my-namespace", +# truncated_namespace: nil, +# streaming_enabled: true # # Configure S3 credentials: # config :ex_aws, :s3, diff --git a/test/config/emoji.txt b/test/config/emoji.txt new file mode 100644 index 000000000..14dd0c332 --- /dev/null +++ b/test/config/emoji.txt @@ -0,0 +1 @@ +external_emoji, https://example.com/emoji.png diff --git a/test/credo/check/consistency/file_location.ex b/test/credo/check/consistency/file_location.ex index 500983608..abc55fffc 100644 --- a/test/credo/check/consistency/file_location.ex +++ b/test/credo/check/consistency/file_location.ex @@ -1,7 +1,7 @@ # Pleroma: A lightweight social networking server # Originally taken from # https://github.com/VeryBigThings/elixir_common/blob/master/lib/vbt/credo/check/consistency/file_location.ex -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Credo.Check.Consistency.FileLocation do diff --git a/test/fixtures/bridgy/actor.json b/test/fixtures/bridgy/actor.json new file mode 100644 index 000000000..5b2d8982b --- /dev/null +++ b/test/fixtures/bridgy/actor.json @@ -0,0 +1,80 @@ +{ + "id": "https://fed.brid.gy/jk.nipponalba.scot", + "url": "https://fed.brid.gy/r/https://jk.nipponalba.scot", + "urls": [ + { + "value": "https://jk.nipponalba.scot" + }, + { + "value": "https://social.nipponalba.scot/jk" + }, + { + "value": "https://px.nipponalba.scot/jk" + } + ], + "@context": "https://www.w3.org/ns/activitystreams", + "type": "Person", + "name": "J K 🇯🇵🏴󠁧󠁢󠁳󠁣󠁴󠁿", + "image": [ + { + "url": "https://jk.nipponalba.scot/images/profile.jpg", + "type": "Image", + "name": "profile picture" + } + ], + "tag": [ + { + "type": "Tag", + "name": "Craft Beer" + }, + { + "type": "Tag", + "name": "Single Malt Whisky" + }, + { + "type": "Tag", + "name": "Homebrewing" + }, + { + "type": "Tag", + "name": "Scottish Politics" + }, + { + "type": "Tag", + "name": "Scottish History" + }, + { + "type": "Tag", + "name": "Japanese History" + }, + { + "type": "Tag", + "name": "Tech" + }, + { + "type": "Tag", + "name": "Veganism" + }, + { + "type": "Tag", + "name": "Cooking" + } + ], + "icon": [ + { + "url": "https://jk.nipponalba.scot/images/profile.jpg", + "type": "Image", + "name": "profile picture" + } + ], + "preferredUsername": "jk.nipponalba.scot", + "summary": "", + "publicKey": { + "id": "jk.nipponalba.scot", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdarxwzxnNbJ2hneWOYHkYJowk\npyigQtxlUd0VjgSQHwxU9kWqfbrHBVADyTtcqi/4dAzQd3UnCI1TPNnn4LPZY9PW\noiWd3Zl1/EfLFxO7LU9GS7fcSLQkyj5JNhSlN3I8QPudZbybrgRDVZYooDe1D+52\n5KLGqC2ajrIVOiDRTQIDAQAB\n-----END PUBLIC KEY-----" + }, + "inbox": "https://fed.brid.gy/jk.nipponalba.scot/inbox", + "outbox": "https://fed.brid.gy/jk.nipponalba.scot/outbox", + "following": "https://fed.brid.gy/jk.nipponalba.scot/following", + "followers": "https://fed.brid.gy/jk.nipponalba.scot/followers" +} diff --git a/test/fixtures/config/temp.exported_from_db.secret.exs b/test/fixtures/config/temp.exported_from_db.secret.exs new file mode 100644 index 000000000..64bee7f32 --- /dev/null +++ b/test/fixtures/config/temp.exported_from_db.secret.exs @@ -0,0 +1,5 @@ +use Mix.Config + +config :pleroma, exported_config_merged: true + +config :pleroma, :first_setting, key: "new value" diff --git a/test/fixtures/config/temp.secret.exs b/test/fixtures/config/temp.secret.exs index 621bc8cf6..4b3af39ec 100644 --- a/test/fixtures/config/temp.secret.exs +++ b/test/fixtures/config/temp.secret.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only use Mix.Config diff --git a/test/fixtures/guppe-actor.json b/test/fixtures/guppe-actor.json new file mode 100644 index 000000000..d5829ee1f --- /dev/null +++ b/test/fixtures/guppe-actor.json @@ -0,0 +1,26 @@ +{ + "@context" : [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1" + ], + "followers" : "https://gup.pe/u/bernie2020/followers", + "following" : "https://gup.pe/u/bernie2020/following", + "icon" : { + "mediaType" : "image/jpeg", + "type" : "Image", + "url" : "https://gup.pe/f/guppe.png" + }, + "id" : "https://gup.pe/u/bernie2020", + "inbox" : "https://gup.pe/u/bernie2020/inbox", + "liked" : "https://gup.pe/u/bernie2020/liked", + "name" : "Bernie2020 group", + "outbox" : "https://gup.pe/u/bernie2020/outbox", + "preferredUsername" : "Bernie2020", + "publicKey" : { + "id" : "https://gup.pe/u/bernie2020#main-key", + "owner" : "https://gup.pe/u/bernie2020", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw4J8nSrdWWxFaipgWDhR\nbTFzHUGoFy7Gjdc6gg9ZWGWDm9ZU5Ct0C/4o72dXSWdyLbQGYMbWVHLI1LHWKSiC\nVtwIYoccQBaxfi5bCxsahWhhSNPfK8tVlySHvBy73ir8KUZm93eAYh1iE9x+Dk63\nInmi7wzjsqHSlu1KxPGYcnyxs+xxhlTUSd5LsPfO1b9sHMW+X4rEky7OC90veCdD\nsoHU+nCmf+2zJSlOrU7DAzqB4Axc9oS9Q5RlT3yARJQMeu6JyjJJP9CMbpGFbUNT\n5Gsw0km1Rc1rR4tUoz8pLUYtliEUK+/0EmHi2EHAT1ueEfMoGGbCaX/mCoMmAwYJ\nwIGYXmKn2/ARIJpw2XPmrKWXqa2AndOQdb3l44Sl3ej2rC/JQmimGCn7tbfKEZyC\n6mMkOYTIeBtyW/wXFc1+GzJxtvA3C9HjilE+O/7gLHfCLP6FRIxg/9kOLhEj64Ed\n5HZ3sylvifXXubS/lLZr6sZW6d9ICoYLZpFw9AoF2zaYWpvJqBrWinnCJzvbMCYj\nfq/RAkcQYSxkDOHquiGgbRZHGAMKLnz5fMKJIzBtdQojYCUmB14OArW+ITUE9i2a\nPAJaXEGZ+BHYp/0ScFaXwp5LIgT1S+sPKxWJU//77wQfs25i7NZHSN/jtXVmsFS6\nLFVw49LcWAz3J2Im+A+uSd8CAwEAAQ==\n-----END PUBLIC KEY-----\n" + }, + "summary" : "I'm a group about Bernie2020. Follow me to get all the group posts. Tag me to share with the group. Create other groups by searching for or tagging @yourGroupName@gup.pe", + "type" : "Group" +} diff --git a/test/fixtures/mastodon/collections/featured.json b/test/fixtures/mastodon/collections/featured.json new file mode 100644 index 000000000..56f8f56fa --- /dev/null +++ b/test/fixtures/mastodon/collections/featured.json @@ -0,0 +1,39 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://{{domain}}/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "id": "https://{{domain}}/users/{{nickname}}/collections/featured", + "orderedItems": [ + { + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://{{domain}}/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "actor": "https://{{domain}}/users/{{nickname}}", + "attachment": [], + "attributedTo": "https://{{domain}}/users/{{nickname}}", + "cc": [ + "https://{{domain}}/users/{{nickname}}/followers" + ], + "content": "", + "id": "https://{{domain}}/objects/{{object_id}}", + "published": "2021-02-12T15:13:43.915429Z", + "sensitive": false, + "source": "", + "summary": "", + "tag": [], + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "type": "Note" + } + ], + "type": "OrderedCollection" +} diff --git a/test/fixtures/modules/runtime_module.ex b/test/fixtures/modules/runtime_module.ex index e348c499e..940b58a1b 100644 --- a/test/fixtures/modules/runtime_module.ex +++ b/test/fixtures/modules/runtime_module.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Fixtures.Modules.RuntimeModule do diff --git a/test/fixtures/peertube/actor-person.json b/test/fixtures/peertube/actor-person.json new file mode 100644 index 000000000..8c387d455 --- /dev/null +++ b/test/fixtures/peertube/actor-person.json @@ -0,0 +1,121 @@ +{ + "type": "Person", + "id": "https://peertube.stream/accounts/createurs", + "following": "https://peertube.stream/accounts/createurs/following", + "followers": "https://peertube.stream/accounts/createurs/followers", + "playlists": "https://peertube.stream/accounts/createurs/playlists", + "inbox": "https://peertube.stream/accounts/createurs/inbox", + "outbox": "https://peertube.stream/accounts/createurs/outbox", + "preferredUsername": "createurs", + "url": "https://peertube.stream/accounts/createurs", + "name": "Créateurs", + "endpoints": { + "sharedInbox": "https://peertube.stream/inbox" + }, + "publicKey": { + "id": "https://peertube.stream/accounts/createurs#main-key", + "owner": "https://peertube.stream/accounts/createurs", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxqkQhbRYbA81+WTYjorR\n2lEMad3kYCnzDjGTLr4I92eanzFHxyELGnjzP6TpEvjOiB9NrCRrqU/iFPLdgrq2\nwIFcXPWdCq6Gcg7QLlaeMM0JoJmr0KTEhzg0XKCo96UsyTzaF4DISxqi8RyoyWeU\nEkgiOzlkdYTlouq3MlQH+p1PBAsNUQfIEUsU+l6k1vzbm8JRwlT+D1bNde4I/Lqs\n4uB5ru3zzInwZ2hz9+heiriNoGEBv74rZHYn966tZVX8iMGx2+m6okozEdEQbqCl\n0ekqDcd8P6CoFqqeeu8coh82OUtuFI/XsbetdWA55YQmSHyMiTsIwVbeoogIETbI\n4QIDAQAB\n-----END PUBLIC KEY-----" + }, + "icon": { + "type": "Image", + "mediaType": "image/png", + "url": "https://peertube.stream/lazy-static/avatars/c27b672d-ad8f-498a-adbe-553af8da56f9.png" + }, + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "RsaSignature2017": "https://w3id.org/security#RsaSignature2017" + }, + { + "pt": "https://joinpeertube.org/ns#", + "sc": "http://schema.org#", + "Hashtag": "as:Hashtag", + "uuid": "sc:identifier", + "category": "sc:category", + "licence": "sc:license", + "subtitleLanguage": "sc:subtitleLanguage", + "sensitive": "as:sensitive", + "language": "sc:inLanguage", + "isLiveBroadcast": "sc:isLiveBroadcast", + "liveSaveReplay": { + "@type": "sc:Boolean", + "@id": "pt:liveSaveReplay" + }, + "permanentLive": { + "@type": "sc:Boolean", + "@id": "pt:permanentLive" + }, + "Infohash": "pt:Infohash", + "Playlist": "pt:Playlist", + "PlaylistElement": "pt:PlaylistElement", + "originallyPublishedAt": "sc:datePublished", + "views": { + "@type": "sc:Number", + "@id": "pt:views" + }, + "state": { + "@type": "sc:Number", + "@id": "pt:state" + }, + "size": { + "@type": "sc:Number", + "@id": "pt:size" + }, + "fps": { + "@type": "sc:Number", + "@id": "pt:fps" + }, + "startTimestamp": { + "@type": "sc:Number", + "@id": "pt:startTimestamp" + }, + "stopTimestamp": { + "@type": "sc:Number", + "@id": "pt:stopTimestamp" + }, + "position": { + "@type": "sc:Number", + "@id": "pt:position" + }, + "commentsEnabled": { + "@type": "sc:Boolean", + "@id": "pt:commentsEnabled" + }, + "downloadEnabled": { + "@type": "sc:Boolean", + "@id": "pt:downloadEnabled" + }, + "waitTranscoding": { + "@type": "sc:Boolean", + "@id": "pt:waitTranscoding" + }, + "support": { + "@type": "sc:Text", + "@id": "pt:support" + }, + "likes": { + "@id": "as:likes", + "@type": "@id" + }, + "dislikes": { + "@id": "as:dislikes", + "@type": "@id" + }, + "playlists": { + "@id": "pt:playlists", + "@type": "@id" + }, + "shares": { + "@id": "as:shares", + "@type": "@id" + }, + "comments": { + "@id": "as:comments", + "@type": "@id" + } + } + ], + "summary": null +} diff --git a/test/fixtures/peertube/video-object-mpegURL-only.json b/test/fixtures/peertube/video-object-mpegURL-only.json new file mode 100644 index 000000000..7f26e89bf --- /dev/null +++ b/test/fixtures/peertube/video-object-mpegURL-only.json @@ -0,0 +1,413 @@ +{ + "type": "Create", + "id": "https://peertube.stream/videos/watch/abece3c3-b9c6-47f4-8040-f3eed8c602e6/activity", + "actor": "https://peertube.stream/accounts/createurs", + "object": { + "type": "Video", + "id": "https://peertube.stream/videos/watch/abece3c3-b9c6-47f4-8040-f3eed8c602e6", + "name": "Vu du 20/02/21 : \"Planète Mars 2050\"", + "duration": "PT385S", + "uuid": "abece3c3-b9c6-47f4-8040-f3eed8c602e6", + "tag": [ + { + "type": "Hashtag", + "name": "France3" + }, + { + "type": "Hashtag", + "name": "lezapping" + } + ], + "category": { + "identifier": "11", + "name": "News & Politics" + }, + "language": { + "identifier": "fr", + "name": "French" + }, + "views": 5, + "sensitive": false, + "waitTranscoding": false, + "isLiveBroadcast": false, + "liveSaveReplay": null, + "permanentLive": null, + "state": 1, + "commentsEnabled": true, + "downloadEnabled": false, + "published": "2021-02-20T17:04:54.278Z", + "originallyPublishedAt": "2021-02-19T23:00:00.000Z", + "updated": "2021-02-21T20:01:11.189Z", + "mediaType": "text/markdown", + "content": "Un regard impertinent et libre, orchestré par Patrick Menais et son équipe, sur le monde de l’image.\r\n\r\nEn avant-première du lundi au samedi à 17h00 sur Facebook, Twitter et YouTube.\r\n\r\nDu lundi au samedi à 20h00 sur France 3.\r\n\r\nhttps://www.facebook.com/vufrancetv\r\nhttps://twitter.com/VuFrancetv", + "support": "Suivre VU :\r\n- Twitter : https://twitter.com/vufrancetv\r\n- Facebook :https://www.facebook.com/vufrancetv/\r\n- Site : https://www.france.tv/france-3/vu/", + "subtitleLanguage": [], + "icon": [ + { + "type": "Image", + "url": "https://peertube.stream/static/thumbnails/abece3c3-b9c6-47f4-8040-f3eed8c602e6.jpg", + "mediaType": "image/jpeg", + "width": 223, + "height": 122 + }, + { + "type": "Image", + "url": "https://peertube.stream/lazy-static/previews/abece3c3-b9c6-47f4-8040-f3eed8c602e6.jpg", + "mediaType": "image/jpeg", + "width": 850, + "height": 480 + } + ], + "url": [ + { + "type": "Link", + "mediaType": "text/html", + "href": "https://peertube.stream/videos/watch/abece3c3-b9c6-47f4-8040-f3eed8c602e6" + }, + { + "type": "Link", + "mediaType": "application/x-mpegURL", + "href": "https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/master.m3u8", + "tag": [ + { + "type": "Infohash", + "name": "00bfce9595e1655d8696b60e19ca25c34be5fa63" + }, + { + "type": "Infohash", + "name": "256c21b65d5e0f944b4b79d8e0cbc55c9d906807" + }, + { + "type": "Infohash", + "name": "fcd981098c484d0e328927c8fb21ecf986880b7e" + }, + { + "type": "Infohash", + "name": "f7e01ac566e9fef91cd22514e6c3c256af7a9f5f" + }, + { + "type": "Infohash", + "name": "42b421fc44d0dceb45ac3f6f6419b07fd570a232" + }, + { + "type": "Infohash", + "name": "f876c6d6d49ce618a880ca223df54cb29f4b4bfd" + }, + { + "type": "Link", + "name": "sha256", + "mediaType": "application/json", + "href": "https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/segments-sha256.json" + }, + { + "type": "Link", + "mediaType": "video/mp4", + "href": "https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/abece3c3-b9c6-47f4-8040-f3eed8c602e6-1080-fragmented.mp4", + "height": 1080, + "size": 57888169, + "fps": 25 + }, + { + "type": "Link", + "rel": [ + "metadata", + "video/mp4" + ], + "mediaType": "application/json", + "href": "https://peertube.stream/api/v1/videos/abece3c3-b9c6-47f4-8040-f3eed8c602e6/metadata/570040", + "height": 1080, + "fps": 25 + }, + { + "type": "Link", + "mediaType": "application/x-bittorrent", + "href": "https://peertube.stream/static/torrents/abece3c3-b9c6-47f4-8040-f3eed8c602e6-1080-hls.torrent", + "height": 1080 + }, + { + "type": "Link", + "mediaType": "application/x-bittorrent;x-scheme-handler/magnet", + "href": "magnet:?xs=https%3A%2F%2Fpeertube.stream%2Fstatic%2Ftorrents%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6-1080-hls.torrent&xt=urn:btih:68af82ebcd9df8335e407b755f38f5fd39c8a6a4&dn=Vu+du+20%2F02%2F21+%3A+%22Plan%C3%A8te+Mars+2050%22&tr=wss%3A%2F%2Fpeertube.stream%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.stream%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.stream%2Fstatic%2Fstreaming-playlists%2Fhls%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6-1080-fragmented.mp4", + "height": 1080 + }, + { + "type": "Link", + "mediaType": "video/mp4", + "href": "https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/abece3c3-b9c6-47f4-8040-f3eed8c602e6-720-fragmented.mp4", + "height": 720, + "size": 45165123, + "fps": 25 + }, + { + "type": "Link", + "rel": [ + "metadata", + "video/mp4" + ], + "mediaType": "application/json", + "href": "https://peertube.stream/api/v1/videos/abece3c3-b9c6-47f4-8040-f3eed8c602e6/metadata/570056", + "height": 720, + "fps": 25 + }, + { + "type": "Link", + "mediaType": "application/x-bittorrent", + "href": "https://peertube.stream/static/torrents/abece3c3-b9c6-47f4-8040-f3eed8c602e6-720-hls.torrent", + "height": 720 + }, + { + "type": "Link", + "mediaType": "application/x-bittorrent;x-scheme-handler/magnet", + "href": "magnet:?xs=https%3A%2F%2Fpeertube.stream%2Fstatic%2Ftorrents%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6-720-hls.torrent&xt=urn:btih:8450928a4ffb2a4c5f927a163487c48c05f6e700&dn=Vu+du+20%2F02%2F21+%3A+%22Plan%C3%A8te+Mars+2050%22&tr=wss%3A%2F%2Fpeertube.stream%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.stream%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.stream%2Fstatic%2Fstreaming-playlists%2Fhls%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6-720-fragmented.mp4", + "height": 720 + }, + { + "type": "Link", + "mediaType": "video/mp4", + "href": "https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/abece3c3-b9c6-47f4-8040-f3eed8c602e6-480-fragmented.mp4", + "height": 480, + "size": 29618534, + "fps": 25 + }, + { + "type": "Link", + "rel": [ + "metadata", + "video/mp4" + ], + "mediaType": "application/json", + "href": "https://peertube.stream/api/v1/videos/abece3c3-b9c6-47f4-8040-f3eed8c602e6/metadata/570042", + "height": 480, + "fps": 25 + }, + { + "type": "Link", + "mediaType": "application/x-bittorrent", + "href": "https://peertube.stream/static/torrents/abece3c3-b9c6-47f4-8040-f3eed8c602e6-480-hls.torrent", + "height": 480 + }, + { + "type": "Link", + "mediaType": "application/x-bittorrent;x-scheme-handler/magnet", + "href": "magnet:?xs=https%3A%2F%2Fpeertube.stream%2Fstatic%2Ftorrents%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6-480-hls.torrent&xt=urn:btih:39e11181db5f376aa78c94bffcb9ccf2f4bca715&dn=Vu+du+20%2F02%2F21+%3A+%22Plan%C3%A8te+Mars+2050%22&tr=wss%3A%2F%2Fpeertube.stream%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.stream%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.stream%2Fstatic%2Fstreaming-playlists%2Fhls%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6-480-fragmented.mp4", + "height": 480 + }, + { + "type": "Link", + "mediaType": "video/mp4", + "href": "https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/abece3c3-b9c6-47f4-8040-f3eed8c602e6-360-fragmented.mp4", + "height": 360, + "size": 21771466, + "fps": 25 + }, + { + "type": "Link", + "rel": [ + "metadata", + "video/mp4" + ], + "mediaType": "application/json", + "href": "https://peertube.stream/api/v1/videos/abece3c3-b9c6-47f4-8040-f3eed8c602e6/metadata/570043", + "height": 360, + "fps": 25 + }, + { + "type": "Link", + "mediaType": "application/x-bittorrent", + "href": "https://peertube.stream/static/torrents/abece3c3-b9c6-47f4-8040-f3eed8c602e6-360-hls.torrent", + "height": 360 + }, + { + "type": "Link", + "mediaType": "application/x-bittorrent;x-scheme-handler/magnet", + "href": "magnet:?xs=https%3A%2F%2Fpeertube.stream%2Fstatic%2Ftorrents%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6-360-hls.torrent&xt=urn:btih:c33aa52822528e29ffd1a615ebe40450e4c61452&dn=Vu+du+20%2F02%2F21+%3A+%22Plan%C3%A8te+Mars+2050%22&tr=wss%3A%2F%2Fpeertube.stream%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.stream%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.stream%2Fstatic%2Fstreaming-playlists%2Fhls%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6-360-fragmented.mp4", + "height": 360 + }, + { + "type": "Link", + "mediaType": "video/mp4", + "href": "https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/abece3c3-b9c6-47f4-8040-f3eed8c602e6-240-fragmented.mp4", + "height": 240, + "size": 14856165, + "fps": 25 + }, + { + "type": "Link", + "rel": [ + "metadata", + "video/mp4" + ], + "mediaType": "application/json", + "href": "https://peertube.stream/api/v1/videos/abece3c3-b9c6-47f4-8040-f3eed8c602e6/metadata/570057", + "height": 240, + "fps": 25 + }, + { + "type": "Link", + "mediaType": "application/x-bittorrent", + "href": "https://peertube.stream/static/torrents/abece3c3-b9c6-47f4-8040-f3eed8c602e6-240-hls.torrent", + "height": 240 + }, + { + "type": "Link", + "mediaType": "application/x-bittorrent;x-scheme-handler/magnet", + "href": "magnet:?xs=https%3A%2F%2Fpeertube.stream%2Fstatic%2Ftorrents%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6-240-hls.torrent&xt=urn:btih:157e4cc3e9f15c06e995d6c3388539fdda312771&dn=Vu+du+20%2F02%2F21+%3A+%22Plan%C3%A8te+Mars+2050%22&tr=wss%3A%2F%2Fpeertube.stream%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.stream%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.stream%2Fstatic%2Fstreaming-playlists%2Fhls%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6-240-fragmented.mp4", + "height": 240 + }, + { + "type": "Link", + "mediaType": "video/mp4", + "href": "https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/abece3c3-b9c6-47f4-8040-f3eed8c602e6-0-fragmented.mp4", + "height": 0, + "size": 6248765, + "fps": 0 + }, + { + "type": "Link", + "rel": [ + "metadata", + "video/mp4" + ], + "mediaType": "application/json", + "href": "https://peertube.stream/api/v1/videos/abece3c3-b9c6-47f4-8040-f3eed8c602e6/metadata/570041", + "height": 0, + "fps": 0 + }, + { + "type": "Link", + "mediaType": "application/x-bittorrent", + "href": "https://peertube.stream/static/torrents/abece3c3-b9c6-47f4-8040-f3eed8c602e6-0-hls.torrent", + "height": 0 + }, + { + "type": "Link", + "mediaType": "application/x-bittorrent;x-scheme-handler/magnet", + "href": "magnet:?xs=https%3A%2F%2Fpeertube.stream%2Fstatic%2Ftorrents%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6-0-hls.torrent&xt=urn:btih:abc8dc58903d18cf7ec0c0cef92cc5ffe5cb0b5c&dn=Vu+du+20%2F02%2F21+%3A+%22Plan%C3%A8te+Mars+2050%22&tr=wss%3A%2F%2Fpeertube.stream%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.stream%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.stream%2Fstatic%2Fstreaming-playlists%2Fhls%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6%2Fabece3c3-b9c6-47f4-8040-f3eed8c602e6-0-fragmented.mp4", + "height": 0 + } + ] + } + ], + "likes": "https://peertube.stream/videos/watch/abece3c3-b9c6-47f4-8040-f3eed8c602e6/likes", + "dislikes": "https://peertube.stream/videos/watch/abece3c3-b9c6-47f4-8040-f3eed8c602e6/dislikes", + "shares": "https://peertube.stream/videos/watch/abece3c3-b9c6-47f4-8040-f3eed8c602e6/announces", + "comments": "https://peertube.stream/videos/watch/abece3c3-b9c6-47f4-8040-f3eed8c602e6/comments", + "attributedTo": [ + { + "type": "Person", + "id": "https://peertube.stream/accounts/createurs" + }, + { + "type": "Group", + "id": "https://peertube.stream/video-channels/vu" + } + ], + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://peertube.stream/accounts/createurs/followers" + ] + }, + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://peertube.stream/accounts/createurs/followers" + ], + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "RsaSignature2017": "https://w3id.org/security#RsaSignature2017" + }, + { + "pt": "https://joinpeertube.org/ns#", + "sc": "http://schema.org#", + "Hashtag": "as:Hashtag", + "uuid": "sc:identifier", + "category": "sc:category", + "licence": "sc:license", + "subtitleLanguage": "sc:subtitleLanguage", + "sensitive": "as:sensitive", + "language": "sc:inLanguage", + "isLiveBroadcast": "sc:isLiveBroadcast", + "liveSaveReplay": { + "@type": "sc:Boolean", + "@id": "pt:liveSaveReplay" + }, + "permanentLive": { + "@type": "sc:Boolean", + "@id": "pt:permanentLive" + }, + "Infohash": "pt:Infohash", + "Playlist": "pt:Playlist", + "PlaylistElement": "pt:PlaylistElement", + "originallyPublishedAt": "sc:datePublished", + "views": { + "@type": "sc:Number", + "@id": "pt:views" + }, + "state": { + "@type": "sc:Number", + "@id": "pt:state" + }, + "size": { + "@type": "sc:Number", + "@id": "pt:size" + }, + "fps": { + "@type": "sc:Number", + "@id": "pt:fps" + }, + "startTimestamp": { + "@type": "sc:Number", + "@id": "pt:startTimestamp" + }, + "stopTimestamp": { + "@type": "sc:Number", + "@id": "pt:stopTimestamp" + }, + "position": { + "@type": "sc:Number", + "@id": "pt:position" + }, + "commentsEnabled": { + "@type": "sc:Boolean", + "@id": "pt:commentsEnabled" + }, + "downloadEnabled": { + "@type": "sc:Boolean", + "@id": "pt:downloadEnabled" + }, + "waitTranscoding": { + "@type": "sc:Boolean", + "@id": "pt:waitTranscoding" + }, + "support": { + "@type": "sc:Text", + "@id": "pt:support" + }, + "likes": { + "@id": "as:likes", + "@type": "@id" + }, + "dislikes": { + "@id": "as:dislikes", + "@type": "@id" + }, + "playlists": { + "@id": "pt:playlists", + "@type": "@id" + }, + "shares": { + "@id": "as:shares", + "@type": "@id" + }, + "comments": { + "@id": "as:comments", + "@type": "@id" + } + } + ] +} diff --git a/test/fixtures/statuses/masto-note.json b/test/fixtures/statuses/masto-note.json new file mode 100644 index 000000000..6b96de473 --- /dev/null +++ b/test/fixtures/statuses/masto-note.json @@ -0,0 +1,47 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount" + } + ], + "id": "https://example.com/users/{{nickname}}/statuses/{{status_id}}", + "type": "Note", + "summary": null, + "inReplyTo": null, + "published": "2021-02-24T12:40:49Z", + "url": "https://example.com/@{{nickname}}/{{status_id}}", + "attributedTo": "https://example.com/users/{{nickname}}", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://example.com/users/{{nickname}}/followers" + ], + "sensitive": false, + "atomUri": "https://example.com/users/{{nickname}}/statuses/{{status_id}}", + "inReplyToAtomUri": null, + "conversation": "tag:example.com,2021-02-24:objectId=15:objectType=Conversation", + "content": "

", + "contentMap": { + "en": "

" + }, + "attachment": [], + "tag": [], + "replies": { + "id": "https://example.com/users/{{nickname}}/statuses/{{status_id}}/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "https://example.com/users/{{nickname}}/statuses/{{status_id}}/replies?only_other_accounts=true&page=true", + "partOf": "https://example.com/users/{{nickname}}/statuses/{{status_id}}/replies", + "items": [] + } + } +} diff --git a/test/fixtures/statuses/note.json b/test/fixtures/statuses/note.json new file mode 100644 index 000000000..41735cbc5 --- /dev/null +++ b/test/fixtures/statuses/note.json @@ -0,0 +1,27 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://example.com/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "actor": "https://example.com/users/{{nickname}}", + "attachment": [], + "attributedTo": "https://example.com/users/{{nickname}}", + "cc": [ + "https://example.com/users/{{nickname}}/followers" + ], + "content": "Content", + "context": "https://example.com/contexts/e4b180e1-7403-477f-aeb4-de57e7a3fe7f", + "conversation": "https://example.com/contexts/e4b180e1-7403-477f-aeb4-de57e7a3fe7f", + "id": "https://example.com/objects/{{object_id}}", + "published": "2019-12-15T22:00:05.279583Z", + "sensitive": false, + "summary": "", + "tag": [], + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "type": "Note" +} diff --git a/test/fixtures/tesla_mock/emoji-in-summary.json b/test/fixtures/tesla_mock/emoji-in-summary.json new file mode 100644 index 000000000..f77c6e2e8 --- /dev/null +++ b/test/fixtures/tesla_mock/emoji-in-summary.json @@ -0,0 +1,49 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://patch.cx/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "actor": "https://patch.cx/users/rin", + "attachment": [], + "attributedTo": "https://patch.cx/users/rin", + "cc": [ + "https://patch.cx/users/rin/followers" + ], + "content": ":joker_disapprove:

just grabbing a test fixture, nevermind me", + "context": "https://patch.cx/contexts/2c3ce4b4-18b1-4b1a-8965-3932027b5326", + "conversation": "https://patch.cx/contexts/2c3ce4b4-18b1-4b1a-8965-3932027b5326", + "id": "https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f", + "published": "2021-03-22T16:54:46.461939Z", + "sensitive": null, + "source": ":joker_disapprove: \r\n\r\njust grabbing a test fixture, nevermind me", + "summary": ":joker_smile: ", + "tag": [ + { + "icon": { + "type": "Image", + "url": "https://patch.cx/emoji/custom/joker_disapprove.png" + }, + "id": "https://patch.cx/emoji/custom/joker_disapprove.png", + "name": ":joker_disapprove:", + "type": "Emoji", + "updated": "1970-01-01T00:00:00Z" + }, + { + "icon": { + "type": "Image", + "url": "https://patch.cx/emoji/custom/joker_smile.png" + }, + "id": "https://patch.cx/emoji/custom/joker_smile.png", + "name": ":joker_smile:", + "type": "Emoji", + "updated": "1970-01-01T00:00:00Z" + } + ], + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "type": "Note" +} diff --git a/test/fixtures/tesla_mock/xn--q9jyb4c_host_meta b/test/fixtures/tesla_mock/xn--q9jyb4c_host_meta deleted file mode 100644 index 45d260e55..000000000 --- a/test/fixtures/tesla_mock/xn--q9jyb4c_host_meta +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/test/fixtures/users_mock/localhost.json b/test/fixtures/users_mock/localhost.json deleted file mode 100644 index a49935db1..000000000 --- a/test/fixtures/users_mock/localhost.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "@context": [ - "https://www.w3.org/ns/activitystreams", - "http://localhost:4001/schemas/litepub-0.1.jsonld", - { - "@language": "und" - } - ], - "attachment": [], - "endpoints": { - "oauthAuthorizationEndpoint": "http://localhost:4001/oauth/authorize", - "oauthRegistrationEndpoint": "http://localhost:4001/api/v1/apps", - "oauthTokenEndpoint": "http://localhost:4001/oauth/token", - "sharedInbox": "http://localhost:4001/inbox" - }, - "followers": "http://localhost:4001/users/{{nickname}}/followers", - "following": "http://localhost:4001/users/{{nickname}}/following", - "icon": { - "type": "Image", - "url": "http://localhost:4001/media/4e914f5b84e4a259a3f6c2d2edc9ab642f2ab05f3e3d9c52c81fc2d984b3d51e.jpg" - }, - "id": "http://localhost:4001/users/{{nickname}}", - "image": { - "type": "Image", - "url": "http://localhost:4001/media/f739efddefeee49c6e67e947c4811fdc911785c16ae43da4c3684051fbf8da6a.jpg?name=f739efddefeee49c6e67e947c4811fdc911785c16ae43da4c3684051fbf8da6a.jpg" - }, - "inbox": "http://localhost:4001/users/{{nickname}}/inbox", - "manuallyApprovesFollowers": false, - "name": "{{nickname}}", - "outbox": "http://localhost:4001/users/{{nickname}}/outbox", - "preferredUsername": "{{nickname}}", - "publicKey": { - "id": "http://localhost:4001/users/{{nickname}}#main-key", - "owner": "http://localhost:4001/users/{{nickname}}", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5DLtwGXNZElJyxFGfcVc\nXANhaMadj/iYYQwZjOJTV9QsbtiNBeIK54PJrYuU0/0YIdrvS1iqheX5IwXRhcwa\nhm3ZyLz7XeN9st7FBni4BmZMBtMpxAuYuu5p/jbWy13qAiYOhPreCx0wrWgm/lBD\n9mkgaxIxPooBE0S4ZWEJIDIV1Vft3AWcRUyWW1vIBK0uZzs6GYshbQZB952S0yo4\nFzI1hABGHncH8UvuFauh4EZ8tY7/X5I0pGRnDOcRN1dAht5w5yTA+6r5kebiFQjP\nIzN/eCO/a9Flrj9YGW7HDNtjSOH0A31PLRGlJtJO3yK57dnf5ppyCZGfL4emShQo\ncQIDAQAB\n-----END PUBLIC KEY-----\n\n" - }, - "summary": "your friendly neighborhood pleroma developer
I like cute things and distributed systems, and really hate delete and redrafts", - "tag": [], - "type": "Person", - "url": "http://localhost:4001/users/{{nickname}}" -} \ No newline at end of file diff --git a/test/fixtures/users_mock/masto_featured.json b/test/fixtures/users_mock/masto_featured.json new file mode 100644 index 000000000..646a343ad --- /dev/null +++ b/test/fixtures/users_mock/masto_featured.json @@ -0,0 +1,18 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount" + } + ], + "id": "https://{{domain}}/users/{{nickname}}/collections/featured", + "type": "OrderedCollection", + "totalItems": 0, + "orderedItems": [] +} diff --git a/test/fixtures/users_mock/user.json b/test/fixtures/users_mock/user.json new file mode 100644 index 000000000..c722a1145 --- /dev/null +++ b/test/fixtures/users_mock/user.json @@ -0,0 +1,42 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://example.com/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "attachment": [], + "endpoints": { + "oauthAuthorizationEndpoint": "https://example.com/oauth/authorize", + "oauthRegistrationEndpoint": "https://example.com/api/v1/apps", + "oauthTokenEndpoint": "https://example.com/oauth/token", + "sharedInbox": "https://example.com/inbox" + }, + "followers": "https://example.com/users/{{nickname}}/followers", + "following": "https://example.com/users/{{nickname}}/following", + "icon": { + "type": "Image", + "url": "https://example.com/media/4e914f5b84e4a259a3f6c2d2edc9ab642f2ab05f3e3d9c52c81fc2d984b3d51e.jpg" + }, + "id": "https://example.com/users/{{nickname}}", + "image": { + "type": "Image", + "url": "https://example.com/media/f739efddefeee49c6e67e947c4811fdc911785c16ae43da4c3684051fbf8da6a.jpg?name=f739efddefeee49c6e67e947c4811fdc911785c16ae43da4c3684051fbf8da6a.jpg" + }, + "inbox": "https://example.com/users/{{nickname}}/inbox", + "manuallyApprovesFollowers": false, + "name": "{{nickname}}", + "outbox": "https://example.com/users/{{nickname}}/outbox", + "preferredUsername": "{{nickname}}", + "publicKey": { + "id": "https://example.com/users/{{nickname}}#main-key", + "owner": "https://example.com/users/{{nickname}}", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5DLtwGXNZElJyxFGfcVc\nXANhaMadj/iYYQwZjOJTV9QsbtiNBeIK54PJrYuU0/0YIdrvS1iqheX5IwXRhcwa\nhm3ZyLz7XeN9st7FBni4BmZMBtMpxAuYuu5p/jbWy13qAiYOhPreCx0wrWgm/lBD\n9mkgaxIxPooBE0S4ZWEJIDIV1Vft3AWcRUyWW1vIBK0uZzs6GYshbQZB952S0yo4\nFzI1hABGHncH8UvuFauh4EZ8tY7/X5I0pGRnDOcRN1dAht5w5yTA+6r5kebiFQjP\nIzN/eCO/a9Flrj9YGW7HDNtjSOH0A31PLRGlJtJO3yK57dnf5ppyCZGfL4emShQo\ncQIDAQAB\n-----END PUBLIC KEY-----\n\n" + }, + "featured": "https://example.com/users/{{nickname}}/collections/featured", + "summary": "your friendly neighborhood pleroma developer
I like cute things and distributed systems, and really hate delete and redrafts", + "tag": [], + "type": "Person", + "url": "https://example.com/users/{{nickname}}" +} diff --git a/test/mix/pleroma_test.exs b/test/mix/pleroma_test.exs index c3e47b285..af62cc1d9 100644 --- a/test/mix/pleroma_test.exs +++ b/test/mix/pleroma_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.PleromaTest do diff --git a/test/mix/tasks/pleroma/app_test.exs b/test/mix/tasks/pleroma/app_test.exs index 71a84ac8e..9eabd32af 100644 --- a/test/mix/tasks/pleroma/app_test.exs +++ b/test/mix/tasks/pleroma/app_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.AppTest do diff --git a/test/mix/tasks/pleroma/config_test.exs b/test/mix/tasks/pleroma/config_test.exs index 0280d208d..3ed1e94b8 100644 --- a/test/mix/tasks/pleroma/config_test.exs +++ b/test/mix/tasks/pleroma/config_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.ConfigTest do @@ -200,6 +200,44 @@ test "load a settings with large values and pass to file", %{temp_file: temp_fil end end + describe "migrate_from_db/1" do + setup do: clear_config(:configurable_from_database, true) + + setup do + insert_config_record(:pleroma, :setting_first, key: "value", key2: ["Activity"]) + insert_config_record(:pleroma, :setting_second, key: "value2", key2: [Repo]) + insert_config_record(:quack, :level, :info) + + path = "test/instance_static" + file_path = Path.join(path, "temp.exported_from_db.secret.exs") + + on_exit(fn -> File.rm!(file_path) end) + + [file_path: file_path] + end + + test "with path parameter", %{file_path: file_path} do + MixTask.run(["migrate_from_db", "--env", "temp", "--path", Path.dirname(file_path)]) + + file = File.read!(file_path) + assert file =~ "config :pleroma, :setting_first," + assert file =~ "config :pleroma, :setting_second," + assert file =~ "config :quack, :level, :info" + end + + test "release", %{file_path: file_path} do + clear_config(:release, true) + clear_config(:config_path, file_path) + + MixTask.run(["migrate_from_db", "--env", "temp"]) + + file = File.read!(file_path) + assert file =~ "config :pleroma, :setting_first," + assert file =~ "config :pleroma, :setting_second," + assert file =~ "config :quack, :level, :info" + end + end + describe "operations on database config" do setup do: clear_config(:configurable_from_database, true) diff --git a/test/mix/tasks/pleroma/count_statuses_test.exs b/test/mix/tasks/pleroma/count_statuses_test.exs index c5cd16960..80ec206b8 100644 --- a/test/mix/tasks/pleroma/count_statuses_test.exs +++ b/test/mix/tasks/pleroma/count_statuses_test.exs @@ -1,8 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.CountStatusesTest do + # Uses log capture, has to stay synchronous use Pleroma.DataCase alias Pleroma.User diff --git a/test/mix/tasks/pleroma/database_test.exs b/test/mix/tasks/pleroma/database_test.exs index a4bd41922..7a1a759da 100644 --- a/test/mix/tasks/pleroma/database_test.exs +++ b/test/mix/tasks/pleroma/database_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.DatabaseTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true use Oban.Testing, repo: Pleroma.Repo alias Pleroma.Activity @@ -87,7 +87,8 @@ test "following and followers count are updated" do assert user.follower_count == 3 - assert :ok == Mix.Tasks.Pleroma.Database.run(["update_users_following_followers_counts"]) + assert {:ok, :ok} == + Mix.Tasks.Pleroma.Database.run(["update_users_following_followers_counts"]) user = User.get_by_id(user.id) diff --git a/test/mix/tasks/pleroma/digest_test.exs b/test/mix/tasks/pleroma/digest_test.exs index 69dccb745..4a9e461a9 100644 --- a/test/mix/tasks/pleroma/digest_test.exs +++ b/test/mix/tasks/pleroma/digest_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.DigestTest do diff --git a/test/mix/tasks/pleroma/ecto/migrate_test.exs b/test/mix/tasks/pleroma/ecto/migrate_test.exs index 43df176a1..5bdfd8f30 100644 --- a/test/mix/tasks/pleroma/ecto/migrate_test.exs +++ b/test/mix/tasks/pleroma/ecto/migrate_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-onl defmodule Mix.Tasks.Pleroma.Ecto.MigrateTest do - use Pleroma.DataCase, async: true + use Pleroma.DataCase import ExUnit.CaptureLog require Logger diff --git a/test/mix/tasks/pleroma/ecto/rollback_test.exs b/test/mix/tasks/pleroma/ecto/rollback_test.exs index 0236e35d5..f8a37bd49 100644 --- a/test/mix/tasks/pleroma/ecto/rollback_test.exs +++ b/test/mix/tasks/pleroma/ecto/rollback_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.Ecto.RollbackTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import ExUnit.CaptureLog require Logger @@ -12,7 +12,7 @@ test "ecto.rollback info message" do Logger.configure(level: :warn) assert capture_log(fn -> - Mix.Tasks.Pleroma.Ecto.Rollback.run() + Mix.Tasks.Pleroma.Ecto.Rollback.run(["--env", "test"]) end) =~ "[info] Rollback succesfully" Logger.configure(level: level) diff --git a/test/mix/tasks/pleroma/ecto_test.exs b/test/mix/tasks/pleroma/ecto_test.exs index 3a028df83..0164da5a8 100644 --- a/test/mix/tasks/pleroma/ecto_test.exs +++ b/test/mix/tasks/pleroma/ecto_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.EctoTest do diff --git a/test/mix/tasks/pleroma/email_test.exs b/test/mix/tasks/pleroma/email_test.exs index 9523aefd8..ce68b88de 100644 --- a/test/mix/tasks/pleroma/email_test.exs +++ b/test/mix/tasks/pleroma/email_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.EmailTest do @@ -61,18 +61,18 @@ test "Sends test email with given address" do test "Sends confirmation emails" do local_user1 = insert(:user, %{ - confirmation_pending: true, + is_confirmed: false, confirmation_token: "mytoken", - deactivated: false, + is_active: true, email: "local1@pleroma.com", local: true }) local_user2 = insert(:user, %{ - confirmation_pending: true, + is_confirmed: false, confirmation_token: "mytoken", - deactivated: false, + is_active: true, email: "local2@pleroma.com", local: true }) @@ -88,30 +88,30 @@ test "Sends confirmation emails" do test "Does not send confirmation email to inappropriate users" do # confirmed user insert(:user, %{ - confirmation_pending: false, + is_confirmed: true, confirmation_token: "mytoken", - deactivated: false, + is_active: true, email: "confirmed@pleroma.com", local: true }) # remote user insert(:user, %{ - deactivated: false, + is_active: true, email: "remote@not-pleroma.com", local: false }) # deactivated user = insert(:user, %{ - deactivated: true, + is_active: false, email: "deactivated@pleroma.com", local: false }) # invisible user insert(:user, %{ - deactivated: false, + is_active: true, email: "invisible@pleroma.com", local: true, invisible: true diff --git a/test/mix/tasks/pleroma/emoji_test.exs b/test/mix/tasks/pleroma/emoji_test.exs index 0fb8603ac..bd20f285c 100644 --- a/test/mix/tasks/pleroma/emoji_test.exs +++ b/test/mix/tasks/pleroma/emoji_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.EmojiTest do diff --git a/test/mix/tasks/pleroma/frontend_test.exs b/test/mix/tasks/pleroma/frontend_test.exs index 6f9ec14cd..aa4b25ebb 100644 --- a/test/mix/tasks/pleroma/frontend_test.exs +++ b/test/mix/tasks/pleroma/frontend_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.FrontendTest do diff --git a/test/mix/tasks/pleroma/instance_test.exs b/test/mix/tasks/pleroma/instance_test.exs index 6580fc932..5a5a68053 100644 --- a/test/mix/tasks/pleroma/instance_test.exs +++ b/test/mix/tasks/pleroma/instance_test.exs @@ -1,9 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.InstanceTest do - use ExUnit.Case + # Modifies the Application Environment, has to stay synchronous. + use Pleroma.DataCase setup do File.mkdir_p!(tmp_path()) @@ -15,15 +16,17 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do if File.exists?(static_dir) do File.rm_rf(Path.join(static_dir, "robots.txt")) end - - Pleroma.Config.put([:instance, :static_dir], static_dir) end) + # Is being modified by the mix task. + clear_config([:instance, :static_dir]) + :ok end + @uuid Ecto.UUID.generate() defp tmp_path do - "/tmp/generated_files/" + "/tmp/generated_files/#{@uuid}/" end test "running gen" do diff --git a/test/mix/tasks/pleroma/refresh_counter_cache_test.exs b/test/mix/tasks/pleroma/refresh_counter_cache_test.exs index 6a1a9ac17..fe9e5cfeb 100644 --- a/test/mix/tasks/pleroma/refresh_counter_cache_test.exs +++ b/test/mix/tasks/pleroma/refresh_counter_cache_test.exs @@ -1,8 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.RefreshCounterCacheTest do + # Uses log capture, has to stay synchronous use Pleroma.DataCase alias Pleroma.Web.CommonAPI import ExUnit.CaptureIO, only: [capture_io: 1] diff --git a/test/mix/tasks/pleroma/relay_test.exs b/test/mix/tasks/pleroma/relay_test.exs index cf48e7dda..db75b3630 100644 --- a/test/mix/tasks/pleroma/relay_test.exs +++ b/test/mix/tasks/pleroma/relay_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.RelayTest do @@ -100,7 +100,7 @@ test "unfollow when relay is dead" do end) Pleroma.Repo.delete(user) - Cachex.clear(:user_cache) + User.invalidate_cache(user) Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance]) @@ -137,7 +137,7 @@ test "force unfollow when relay is dead" do end) Pleroma.Repo.delete(user) - Cachex.clear(:user_cache) + User.invalidate_cache(user) Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance, "--force"]) diff --git a/test/mix/tasks/pleroma/robots_txt_test.exs b/test/mix/tasks/pleroma/robots_txt_test.exs index 7040a0e4e..028aa4ccc 100644 --- a/test/mix/tasks/pleroma/robots_txt_test.exs +++ b/test/mix/tasks/pleroma/robots_txt_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.RobotsTxtTest do @@ -12,7 +12,7 @@ defmodule Mix.Tasks.Pleroma.RobotsTxtTest do test "creates new dir" do path = "test/fixtures/new_dir/" file_path = path <> "robots.txt" - Pleroma.Config.put([:instance, :static_dir], path) + clear_config([:instance, :static_dir], path) on_exit(fn -> {:ok, ["test/fixtures/new_dir/", "test/fixtures/new_dir/robots.txt"]} = File.rm_rf(path) @@ -29,7 +29,7 @@ test "creates new dir" do test "to existance folder" do path = "test/fixtures/" file_path = path <> "robots.txt" - Pleroma.Config.put([:instance, :static_dir], path) + clear_config([:instance, :static_dir], path) on_exit(fn -> :ok = File.rm(file_path) diff --git a/test/mix/tasks/pleroma/uploads_test.exs b/test/mix/tasks/pleroma/uploads_test.exs index d69e149a8..a7d15e0fa 100644 --- a/test/mix/tasks/pleroma/uploads_test.exs +++ b/test/mix/tasks/pleroma/uploads_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.UploadsTest do diff --git a/test/mix/tasks/pleroma/user_test.exs b/test/mix/tasks/pleroma/user_test.exs index 127771212..a2178bbd1 100644 --- a/test/mix/tasks/pleroma/user_test.exs +++ b/test/mix/tasks/pleroma/user_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.UserTest do @@ -102,7 +102,7 @@ test "user is deleted" do assert_received {:mix_shell, :info, [message]} assert message =~ " deleted" - assert %{deactivated: true} = User.get_by_nickname(user.nickname) + assert %{is_active: false} = User.get_by_nickname(user.nickname) assert called(Pleroma.Web.Federator.publish(:_)) end @@ -114,7 +114,7 @@ test "a remote user's create activity is deleted when the object has been pruned {:ok, post} = CommonAPI.post(user, %{status: "uguu"}) {:ok, post2} = CommonAPI.post(user2, %{status: "test"}) - obj = Object.normalize(post2) + obj = Object.normalize(post2, fetch: false) {:ok, like_object, meta} = Pleroma.Web.ActivityPub.Builder.like(user, obj) @@ -130,7 +130,7 @@ test "a remote user's create activity is deleted when the object has been pruned clear_config([:instance, :federating], true) - object = Object.normalize(post) + object = Object.normalize(post, fetch: false) Object.prune(object) with_mock Pleroma.Web.Federator, @@ -140,7 +140,7 @@ test "a remote user's create activity is deleted when the object has been pruned assert_received {:mix_shell, :info, [message]} assert message =~ " deleted" - assert %{deactivated: true} = User.get_by_nickname(user.nickname) + assert %{is_active: false} = User.get_by_nickname(user.nickname) assert called(Pleroma.Web.Federator.publish(:_)) refute Pleroma.Repo.get(Pleroma.Activity, like_activity.id) @@ -157,41 +157,8 @@ test "no user to delete" do end end - describe "running toggle_activated" do - test "user is deactivated" do - user = insert(:user) - - Mix.Tasks.Pleroma.User.run(["toggle_activated", user.nickname]) - - assert_received {:mix_shell, :info, [message]} - assert message =~ " deactivated" - - user = User.get_cached_by_nickname(user.nickname) - assert user.deactivated - end - - test "user is activated" do - user = insert(:user, deactivated: true) - - Mix.Tasks.Pleroma.User.run(["toggle_activated", user.nickname]) - - assert_received {:mix_shell, :info, [message]} - assert message =~ " activated" - - user = User.get_cached_by_nickname(user.nickname) - refute user.deactivated - end - - test "no user to toggle" do - Mix.Tasks.Pleroma.User.run(["toggle_activated", "nonexistent"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "No user" - end - end - describe "running deactivate" do - test "user is unsubscribed" do + test "active user is deactivated and unsubscribed" do followed = insert(:user) remote_followed = insert(:user, local: false) user = insert(:user) @@ -201,16 +168,26 @@ test "user is unsubscribed" do Mix.Tasks.Pleroma.User.run(["deactivate", user.nickname]) - assert_received {:mix_shell, :info, [message]} - assert message =~ "Deactivating" - # Note that the task has delay :timer.sleep(500) assert_received {:mix_shell, :info, [message]} - assert message =~ "Successfully unsubscribed" + + assert message == + "Successfully deactivated #{user.nickname} and unsubscribed all local followers" user = User.get_cached_by_nickname(user.nickname) assert Enum.empty?(Enum.filter(User.get_friends(user), & &1.local)) - assert user.deactivated + refute user.is_active + end + + test "user is deactivated" do + %{id: id, nickname: nickname} = insert(:user, is_active: false) + + assert :ok = Mix.Tasks.Pleroma.User.run(["deactivate", nickname]) + assert_received {:mix_shell, :info, [message]} + assert message == "User #{nickname} already deactivated" + + user = Repo.get(User, id) + refute user.is_active end test "no user to deactivate" do @@ -238,7 +215,7 @@ test "All statuses set" do assert message =~ ~r/Admin status .* true/ assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Confirmation pending .* false/ + assert message =~ ~r/Confirmation status.* true/ assert_received {:mix_shell, :info, [message]} assert message =~ ~r/Locked status .* true/ @@ -250,7 +227,7 @@ test "All statuses set" do assert user.is_moderator assert user.is_locked assert user.is_admin - refute user.confirmation_pending + assert user.is_confirmed end test "All statuses unset" do @@ -259,7 +236,7 @@ test "All statuses unset" do is_locked: true, is_moderator: true, is_admin: true, - confirmation_pending: true + is_confirmed: false ) Mix.Tasks.Pleroma.User.run([ @@ -275,7 +252,7 @@ test "All statuses unset" do assert message =~ ~r/Admin status .* false/ assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Confirmation pending .* true/ + assert message =~ ~r/Confirmation status.* false/ assert_received {:mix_shell, :info, [message]} assert message =~ ~r/Locked status .* false/ @@ -287,7 +264,7 @@ test "All statuses unset" do refute user.is_moderator refute user.is_locked refute user.is_admin - assert user.confirmation_pending + refute user.is_confirmed end test "no user to set status" do @@ -436,13 +413,6 @@ test "invite is revoked" do assert_received {:mix_shell, :info, [message]} assert message =~ "Invite for token #{invite.token} was revoked." end - - test "it prints an error message when invite is not exist" do - Mix.Tasks.Pleroma.User.run(["revoke_invite", "foo"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "No invite found" - end end describe "running delete_activities" do @@ -462,40 +432,71 @@ test "it prints an error message when user is not exist" do end end - describe "running toggle_confirmed" do + describe "running confirm" do test "user is confirmed" do - %{id: id, nickname: nickname} = insert(:user, confirmation_pending: false) + %{id: id, nickname: nickname} = insert(:user, is_confirmed: true) - assert :ok = Mix.Tasks.Pleroma.User.run(["toggle_confirmed", nickname]) - assert_received {:mix_shell, :info, [message]} - assert message == "#{nickname} needs confirmation." - - user = Repo.get(User, id) - assert user.confirmation_pending - assert user.confirmation_token - end - - test "user is not confirmed" do - %{id: id, nickname: nickname} = - insert(:user, confirmation_pending: true, confirmation_token: "some token") - - assert :ok = Mix.Tasks.Pleroma.User.run(["toggle_confirmed", nickname]) + assert :ok = Mix.Tasks.Pleroma.User.run(["confirm", nickname]) assert_received {:mix_shell, :info, [message]} assert message == "#{nickname} doesn't need confirmation." user = Repo.get(User, id) - refute user.confirmation_pending + assert user.is_confirmed + refute user.confirmation_token + end + + test "user is not confirmed" do + %{id: id, nickname: nickname} = + insert(:user, is_confirmed: false, confirmation_token: "some token") + + assert :ok = Mix.Tasks.Pleroma.User.run(["confirm", nickname]) + assert_received {:mix_shell, :info, [message]} + assert message == "#{nickname} doesn't need confirmation." + + user = Repo.get(User, id) + assert user.is_confirmed refute user.confirmation_token end test "it prints an error message when user is not exist" do - Mix.Tasks.Pleroma.User.run(["toggle_confirmed", "foo"]) + Mix.Tasks.Pleroma.User.run(["confirm", "foo"]) assert_received {:mix_shell, :error, [message]} assert message =~ "No local user" end end + describe "running activate" do + test "user is activated" do + %{id: id, nickname: nickname} = insert(:user, is_active: true) + + assert :ok = Mix.Tasks.Pleroma.User.run(["activate", nickname]) + assert_received {:mix_shell, :info, [message]} + assert message == "User #{nickname} already activated" + + user = Repo.get(User, id) + assert user.is_active + end + + test "user is not activated" do + %{id: id, nickname: nickname} = insert(:user, is_active: false) + + assert :ok = Mix.Tasks.Pleroma.User.run(["activate", nickname]) + assert_received {:mix_shell, :info, [message]} + assert message == "Successfully activated #{nickname}" + + user = Repo.get(User, id) + assert user.is_active + end + + test "no user to activate" do + Mix.Tasks.Pleroma.User.run(["activate", "foo"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "No user" + end + end + describe "search" do test "it returns users matching" do user = insert(:user) @@ -579,29 +580,29 @@ test "it prints an error message when user is not exist" do describe "bulk confirm and unconfirm" do test "confirm all" do - user1 = insert(:user, confirmation_pending: true) - user2 = insert(:user, confirmation_pending: true) + user1 = insert(:user, is_confirmed: false) + user2 = insert(:user, is_confirmed: false) - assert user1.confirmation_pending - assert user2.confirmation_pending + refute user1.is_confirmed + refute user2.is_confirmed Mix.Tasks.Pleroma.User.run(["confirm_all"]) user1 = User.get_cached_by_nickname(user1.nickname) user2 = User.get_cached_by_nickname(user2.nickname) - refute user1.confirmation_pending - refute user2.confirmation_pending + assert user1.is_confirmed + assert user2.is_confirmed end test "unconfirm all" do - user1 = insert(:user, confirmation_pending: false) - user2 = insert(:user, confirmation_pending: false) - admin = insert(:user, is_admin: true, confirmation_pending: false) - mod = insert(:user, is_moderator: true, confirmation_pending: false) + user1 = insert(:user, is_confirmed: true) + user2 = insert(:user, is_confirmed: true) + admin = insert(:user, is_admin: true, is_confirmed: true) + mod = insert(:user, is_moderator: true, is_confirmed: true) - refute user1.confirmation_pending - refute user2.confirmation_pending + assert user1.is_confirmed + assert user2.is_confirmed Mix.Tasks.Pleroma.User.run(["unconfirm_all"]) @@ -610,10 +611,10 @@ test "unconfirm all" do admin = User.get_cached_by_nickname(admin.nickname) mod = User.get_cached_by_nickname(mod.nickname) - assert user1.confirmation_pending - assert user2.confirmation_pending - refute admin.confirmation_pending - refute mod.confirmation_pending + refute user1.is_confirmed + refute user2.is_confirmed + assert admin.is_confirmed + assert mod.is_confirmed end end end diff --git a/test/pleroma/activity/ir/topics_test.exs b/test/pleroma/activity/ir/topics_test.exs index 5e5c2f8da..9c8e5d932 100644 --- a/test/pleroma/activity/ir/topics_test.exs +++ b/test/pleroma/activity/ir/topics_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity.Ir.TopicsTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Activity alias Pleroma.Activity.Ir.Topics @@ -11,6 +11,8 @@ defmodule Pleroma.Activity.Ir.TopicsTest do require Pleroma.Constants + import Mock + describe "poll answer" do test "produce no topics" do activity = %Activity{object: %Object{data: %{"type" => "Answer"}}} @@ -77,14 +79,13 @@ test "with no attachments doesn't produce public:media topics", %{activity: acti refute Enum.member?(topics, "public:local:media") end - test "converts tags to hash tags", %{activity: %{object: %{data: data} = object} = activity} do - tagged_data = Map.put(data, "tag", ["foo", "bar"]) - activity = %{activity | object: %{object | data: tagged_data}} + test "converts tags to hash tags", %{activity: activity} do + with_mock(Object, [:passthrough], hashtags: fn _ -> ["foo", "bar"] end) do + topics = Topics.get_activity_topics(activity) - topics = Topics.get_activity_topics(activity) - - assert Enum.member?(topics, "hashtag:foo") - assert Enum.member?(topics, "hashtag:bar") + assert Enum.member?(topics, "hashtag:foo") + assert Enum.member?(topics, "hashtag:bar") + end end test "only converts strings to hash tags", %{ diff --git a/test/pleroma/activity/search_test.exs b/test/pleroma/activity/search_test.exs index fc910e725..657fbc627 100644 --- a/test/pleroma/activity/search_test.exs +++ b/test/pleroma/activity/search_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Activity.SearchTest do @@ -7,7 +7,7 @@ defmodule Pleroma.Activity.SearchTest do alias Pleroma.Web.CommonAPI import Pleroma.Factory - use Pleroma.DataCase + use Pleroma.DataCase, async: true test "it finds something" do user = insert(:user) diff --git a/test/pleroma/activity_test.exs b/test/pleroma/activity_test.exs index 105f9f766..962bc7e45 100644 --- a/test/pleroma/activity_test.exs +++ b/test/pleroma/activity_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ActivityTest do @@ -25,7 +25,7 @@ test "returns an activity by it's AP id" do test "returns activities by it's objects AP ids" do activity = insert(:note_activity) - object_data = Object.normalize(activity).data + object_data = Object.normalize(activity, fetch: false).data [found_activity] = Activity.get_all_create_by_object_ap_id(object_data["id"]) @@ -34,7 +34,7 @@ test "returns activities by it's objects AP ids" do test "returns the activity that created an object" do activity = insert(:note_activity) - object_data = Object.normalize(activity).data + object_data = Object.normalize(activity, fetch: false).data found_activity = Activity.get_create_by_object_ap_id(object_data["id"]) @@ -168,7 +168,7 @@ test "find only local statuses for unauthenticated users", %{local_activity: loc test "find only local statuses for unauthenticated users when `limit_to_local_content` is `:all`", %{local_activity: local_activity} do - Pleroma.Config.put([:instance, :limit_to_local_content], :all) + clear_config([:instance, :limit_to_local_content], :all) assert [^local_activity] = Activity.search(nil, "find me") end @@ -177,7 +177,7 @@ test "find all statuses for unauthenticated users when `limit_to_local_content` local_activity: local_activity, remote_activity: remote_activity } do - Pleroma.Config.put([:instance, :limit_to_local_content], false) + clear_config([:instance, :limit_to_local_content], false) activities = Enum.sort_by(Activity.search(nil, "find me"), & &1.id) @@ -254,4 +254,26 @@ test "get_by_object_ap_id_with_object/1" do assert %{id: ^id} = Activity.get_by_object_ap_id_with_object(obj_id) end + + test "add_by_params_query/3" do + user = insert(:user) + + note = insert(:note_activity, user: user) + + insert(:add_activity, user: user, note: note) + insert(:add_activity, user: user, note: note) + insert(:add_activity, user: user) + + assert Repo.aggregate(Activity, :count, :id) == 4 + + add_query = + Activity.add_by_params_query(note.data["object"], user.ap_id, user.featured_address) + + assert Repo.aggregate(add_query, :count, :id) == 2 + + Repo.delete_all(add_query) + assert Repo.aggregate(add_query, :count, :id) == 0 + + assert Repo.aggregate(Activity, :count, :id) == 2 + end end diff --git a/test/pleroma/application_requirements_test.exs b/test/pleroma/application_requirements_test.exs index b432dbc37..683ac8c96 100644 --- a/test/pleroma/application_requirements_test.exs +++ b/test/pleroma/application_requirements_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ApplicationRequirementsTest do @@ -9,12 +9,12 @@ defmodule Pleroma.ApplicationRequirementsTest do import Mock alias Pleroma.ApplicationRequirements - alias Pleroma.Config alias Pleroma.Repo describe "check_repo_pool_size!/1" do test "raises if the pool size is unexpected" do clear_config([Pleroma.Repo, :pool_size], 11) + clear_config([:dangerzone, :override_repo_pool_size], false) assert_raise Pleroma.ApplicationRequirements.VerifyError, "Repo.pool_size different than recommended value.", @@ -36,8 +36,8 @@ test "doesn't raise if the pool size is unexpected but the respective flag is se setup do: clear_config([Pleroma.Emails.Mailer]) test "raises if welcome email enabled but mail disabled" do - Pleroma.Config.put([:welcome, :email, :enabled], true) - Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false) + clear_config([:welcome, :email, :enabled], true) + clear_config([Pleroma.Emails.Mailer, :enabled], false) assert_raise Pleroma.ApplicationRequirements.VerifyError, "The mail disabled.", fn -> capture_log(&Pleroma.ApplicationRequirements.verify!/0) @@ -58,8 +58,8 @@ test "raises if welcome email enabled but mail disabled" do setup do: clear_config([:instance, :account_activation_required]) test "raises if account confirmation is required but mailer isn't enable" do - Pleroma.Config.put([:instance, :account_activation_required], true) - Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false) + clear_config([:instance, :account_activation_required], true) + clear_config([Pleroma.Emails.Mailer, :enabled], false) assert_raise Pleroma.ApplicationRequirements.VerifyError, "Account activation enabled, but Mailer is disabled. Cannot send confirmation emails.", @@ -69,14 +69,14 @@ test "raises if account confirmation is required but mailer isn't enable" do end test "doesn't do anything if account confirmation is disabled" do - Pleroma.Config.put([:instance, :account_activation_required], false) - Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false) + clear_config([:instance, :account_activation_required], false) + clear_config([Pleroma.Emails.Mailer, :enabled], false) assert Pleroma.ApplicationRequirements.verify!() == :ok end test "doesn't do anything if account confirmation is required and mailer is enabled" do - Pleroma.Config.put([:instance, :account_activation_required], true) - Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], true) + clear_config([:instance, :account_activation_required], true) + clear_config([Pleroma.Emails.Mailer, :enabled], true) assert Pleroma.ApplicationRequirements.verify!() == :ok end end @@ -92,7 +92,7 @@ test "doesn't do anything if account confirmation is required and mailer is enab setup do: clear_config([:database, :rum_enabled]) test "raises if rum is enabled and detects unapplied rum migrations" do - Config.put([:database, :rum_enabled], true) + clear_config([:database, :rum_enabled], true) with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> false end]}]) do assert_raise ApplicationRequirements.VerifyError, @@ -104,7 +104,7 @@ test "raises if rum is enabled and detects unapplied rum migrations" do end test "raises if rum is disabled and detects rum migrations" do - Config.put([:database, :rum_enabled], false) + clear_config([:database, :rum_enabled], false) with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> true end]}]) do assert_raise ApplicationRequirements.VerifyError, @@ -116,7 +116,7 @@ test "raises if rum is disabled and detects rum migrations" do end test "doesn't do anything if rum enabled and applied migrations" do - Config.put([:database, :rum_enabled], true) + clear_config([:database, :rum_enabled], true) with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> true end]}]) do assert ApplicationRequirements.verify!() == :ok @@ -124,7 +124,7 @@ test "doesn't do anything if rum enabled and applied migrations" do end test "doesn't do anything if rum disabled" do - Config.put([:database, :rum_enabled], false) + clear_config([:database, :rum_enabled], false) with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> false end]}]) do assert ApplicationRequirements.verify!() == :ok @@ -160,7 +160,7 @@ test "raises if it detects unapplied migrations" do end test "doesn't do anything if disabled" do - Config.put([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true) + clear_config([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true) assert :ok == ApplicationRequirements.verify!() end diff --git a/test/pleroma/bbs/handler_test.exs b/test/pleroma/bbs/handler_test.exs index e605c2726..3990f8286 100644 --- a/test/pleroma/bbs/handler_test.exs +++ b/test/pleroma/bbs/handler_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.BBS.HandlerTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Activity alias Pleroma.BBS.Handler alias Pleroma.Object @@ -54,7 +54,7 @@ test "posting" do ) assert activity.actor == user.ap_id - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert object.data["content"] == "this is a test post" end @@ -63,7 +63,7 @@ test "replying" do another_user = insert(:user) {:ok, activity} = CommonAPI.post(another_user, %{status: "this is a test post"}) - activity_object = Object.normalize(activity) + activity_object = Object.normalize(activity, fetch: false) output = capture_io(fn -> @@ -82,7 +82,7 @@ test "replying" do assert reply.actor == user.ap_id - reply_object_data = Object.normalize(reply).data + reply_object_data = Object.normalize(reply, fetch: false).data assert reply_object_data["content"] == "this is a reply" assert reply_object_data["inReplyTo"] == activity_object.data["id"] end diff --git a/test/pleroma/bookmark_test.exs b/test/pleroma/bookmark_test.exs index 2726fe7cd..9f64a01c2 100644 --- a/test/pleroma/bookmark_test.exs +++ b/test/pleroma/bookmark_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.BookmarkTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Bookmark alias Pleroma.Web.CommonAPI diff --git a/test/pleroma/captcha_test.exs b/test/pleroma/captcha_test.exs index 1b9f4a12f..fcb585112 100644 --- a/test/pleroma/captcha_test.exs +++ b/test/pleroma/captcha_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.CaptchaTest do @@ -69,7 +69,7 @@ test "new and validate" do describe "Captcha Wrapper" do test "validate" do - Pleroma.Config.put([Pleroma.Captcha, :enabled], true) + clear_config([Pleroma.Captcha, :enabled], true) new = Captcha.new() @@ -80,11 +80,10 @@ test "validate" do assert is_binary(answer) assert :ok = Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", answer) - Cachex.del(:used_captcha_cache, token) end test "doesn't validate invalid answer" do - Pleroma.Config.put([Pleroma.Captcha, :enabled], true) + clear_config([Pleroma.Captcha, :enabled], true) new = Captcha.new() @@ -100,7 +99,7 @@ test "doesn't validate invalid answer" do end test "nil answer_data" do - Pleroma.Config.put([Pleroma.Captcha, :enabled], true) + clear_config([Pleroma.Captcha, :enabled], true) new = Captcha.new() diff --git a/test/pleroma/chat/message_reference_test.exs b/test/pleroma/chat/message_reference_test.exs index aaa7c1ad4..c8db3b450 100644 --- a/test/pleroma/chat/message_reference_test.exs +++ b/test/pleroma/chat/message_reference_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Chat.MessageReferenceTest do diff --git a/test/pleroma/chat_test.exs b/test/pleroma/chat_test.exs index 9e8a9ebf0..a5fd1e02e 100644 --- a/test/pleroma/chat_test.exs +++ b/test/pleroma/chat_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ChatTest do @@ -73,7 +73,8 @@ test "a returning chat will have an updated `update_at` field" do other_user = insert(:user) {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) - :timer.sleep(1500) + {:ok, chat} = time_travel(chat, -2) + {:ok, chat_two} = Chat.bump_or_create(user.id, other_user.ap_id) assert chat.id == chat_two.id diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index f52629f8a..15f4982ea 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.DeprecationWarningsTest do @@ -87,13 +87,22 @@ test "check_hellthread_threshold/0" do end test "check_activity_expiration_config/0" do - clear_config(Pleroma.ActivityExpiration, enabled: true) + clear_config([Pleroma.ActivityExpiration], enabled: true) assert capture_log(fn -> DeprecationWarnings.check_activity_expiration_config() end) =~ "Your config is using old namespace for activity expiration configuration." end + test "check_uploders_s3_public_endpoint/0" do + clear_config([Pleroma.Uploaders.S3], public_endpoint: "https://fake.amazonaws.com/bucket/") + + assert capture_log(fn -> + DeprecationWarnings.check_uploders_s3_public_endpoint() + end) =~ + "Your config is using the old setting for controlling the URL of media uploaded to your S3 bucket." + end + describe "check_gun_pool_options/0" do test "await_up_timeout" do config = Config.get(:connections_pool) diff --git a/test/pleroma/config/holder_test.exs b/test/pleroma/config/holder_test.exs index abcaa27dd..ca4c38995 100644 --- a/test/pleroma/config/holder_test.exs +++ b/test/pleroma/config/holder_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.HolderTest do diff --git a/test/pleroma/config/loader_test.exs b/test/pleroma/config/loader_test.exs index 607572f4e..b34fd70da 100644 --- a/test/pleroma/config/loader_test.exs +++ b/test/pleroma/config/loader_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.LoaderTest do diff --git a/test/pleroma/config/release_runtime_provider_test.exs b/test/pleroma/config/release_runtime_provider_test.exs new file mode 100644 index 000000000..6578d3268 --- /dev/null +++ b/test/pleroma/config/release_runtime_provider_test.exs @@ -0,0 +1,45 @@ +defmodule Pleroma.Config.ReleaseRuntimeProviderTest do + use ExUnit.Case, async: true + + alias Pleroma.Config.ReleaseRuntimeProvider + + describe "load/2" do + test "loads release defaults config and warns about non-existent runtime config" do + ExUnit.CaptureIO.capture_io(fn -> + merged = ReleaseRuntimeProvider.load([], []) + assert merged == Pleroma.Config.Holder.release_defaults() + end) =~ + "!!! Config path is not declared! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file" + end + + test "merged runtime config" do + merged = + ReleaseRuntimeProvider.load([], config_path: "test/fixtures/config/temp.secret.exs") + + assert merged[:pleroma][:first_setting] == [key: "value", key2: [Pleroma.Repo]] + assert merged[:pleroma][:second_setting] == [key: "value2", key2: ["Activity"]] + end + + test "merged exported config" do + ExUnit.CaptureIO.capture_io(fn -> + merged = + ReleaseRuntimeProvider.load([], + exported_config_path: "test/fixtures/config/temp.exported_from_db.secret.exs" + ) + + assert merged[:pleroma][:exported_config_merged] + end) =~ + "!!! Config path is not declared! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file" + end + + test "runtime config is merged with exported config" do + merged = + ReleaseRuntimeProvider.load([], + config_path: "test/fixtures/config/temp.secret.exs", + exported_config_path: "test/fixtures/config/temp.exported_from_db.secret.exs" + ) + + assert merged[:pleroma][:first_setting] == [key2: [Pleroma.Repo], key: "new value"] + end + end +end diff --git a/test/pleroma/config/transfer_task_test.exs b/test/pleroma/config/transfer_task_test.exs index f53829e09..8ae5d3b81 100644 --- a/test/pleroma/config/transfer_task_test.exs +++ b/test/pleroma/config/transfer_task_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Config.TransferTaskTest do diff --git a/test/pleroma/config_db_test.exs b/test/pleroma/config_db_test.exs index 3895e2cda..d42123fb4 100644 --- a/test/pleroma/config_db_test.exs +++ b/test/pleroma/config_db_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ConfigDBTest do diff --git a/test/pleroma/config_test.exs b/test/pleroma/config_test.exs index 1556e4237..3158a2ec8 100644 --- a/test/pleroma/config_test.exs +++ b/test/pleroma/config_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ConfigTest do - use ExUnit.Case + use Pleroma.DataCase test "get/1 with an atom" do assert Pleroma.Config.get(:instance) == Application.get_env(:pleroma, :instance) @@ -30,9 +30,9 @@ test "get/1 with a list of keys" do describe "nil values" do setup do - Pleroma.Config.put(:lorem, nil) - Pleroma.Config.put(:ipsum, %{dolor: [sit: nil]}) - Pleroma.Config.put(:dolor, sit: %{amet: nil}) + clear_config(:lorem, nil) + clear_config(:ipsum, %{dolor: [sit: nil]}) + clear_config(:dolor, sit: %{amet: nil}) on_exit(fn -> Enum.each(~w(lorem ipsum dolor)a, &Pleroma.Config.delete/1) end) end @@ -57,9 +57,9 @@ test "get/2 with a list of keys for nil value" do end test "get/1 when value is false" do - Pleroma.Config.put([:instance, :false_test], false) - Pleroma.Config.put([:instance, :nested], []) - Pleroma.Config.put([:instance, :nested, :false_test], false) + clear_config([:instance, :false_test], false) + clear_config([:instance, :nested], []) + clear_config([:instance, :nested, :false_test], false) assert Pleroma.Config.get([:instance, :false_test]) == false assert Pleroma.Config.get([:instance, :nested, :false_test]) == false @@ -81,40 +81,40 @@ test "get!/1" do end test "get!/1 when value is false" do - Pleroma.Config.put([:instance, :false_test], false) - Pleroma.Config.put([:instance, :nested], []) - Pleroma.Config.put([:instance, :nested, :false_test], false) + clear_config([:instance, :false_test], false) + clear_config([:instance, :nested], []) + clear_config([:instance, :nested, :false_test], false) assert Pleroma.Config.get!([:instance, :false_test]) == false assert Pleroma.Config.get!([:instance, :nested, :false_test]) == false end test "put/2 with a key" do - Pleroma.Config.put(:config_test, true) + clear_config(:config_test, true) assert Pleroma.Config.get(:config_test) == true end test "put/2 with a list of keys" do - Pleroma.Config.put([:instance, :config_test], true) - Pleroma.Config.put([:instance, :config_nested_test], []) - Pleroma.Config.put([:instance, :config_nested_test, :x], true) + clear_config([:instance, :config_test], true) + clear_config([:instance, :config_nested_test], []) + clear_config([:instance, :config_nested_test, :x], true) assert Pleroma.Config.get([:instance, :config_test]) == true assert Pleroma.Config.get([:instance, :config_nested_test, :x]) == true end test "delete/1 with a key" do - Pleroma.Config.put([:delete_me], :delete_me) + clear_config([:delete_me], :delete_me) Pleroma.Config.delete([:delete_me]) assert Pleroma.Config.get([:delete_me]) == nil end test "delete/2 with a list of keys" do - Pleroma.Config.put([:delete_me], hello: "world", world: "Hello") + clear_config([:delete_me], hello: "world", world: "Hello") Pleroma.Config.delete([:delete_me, :world]) assert Pleroma.Config.get([:delete_me]) == [hello: "world"] - Pleroma.Config.put([:delete_me, :delete_me], hello: "world", world: "Hello") + clear_config([:delete_me, :delete_me], hello: "world", world: "Hello") Pleroma.Config.delete([:delete_me, :delete_me, :world]) assert Pleroma.Config.get([:delete_me, :delete_me]) == [hello: "world"] @@ -123,8 +123,8 @@ test "delete/2 with a list of keys" do end test "fetch/1" do - Pleroma.Config.put([:lorem], :ipsum) - Pleroma.Config.put([:ipsum], dolor: :sit) + clear_config([:lorem], :ipsum) + clear_config([:ipsum], dolor: :sit) assert Pleroma.Config.fetch([:lorem]) == {:ok, :ipsum} assert Pleroma.Config.fetch(:lorem) == {:ok, :ipsum} diff --git a/test/pleroma/conversation/participation_test.exs b/test/pleroma/conversation/participation_test.exs index 5a603dcc1..a25e17c95 100644 --- a/test/pleroma/conversation/participation_test.exs +++ b/test/pleroma/conversation/participation_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Conversation.ParticipationTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Conversation alias Pleroma.Conversation.Participation @@ -96,12 +96,11 @@ test "it creates a participation for a conversation and a user" do {:ok, %Participation{} = participation} = Participation.create_for_user_and_conversation(user, conversation) + {:ok, participation} = time_travel(participation, -2) + assert participation.user_id == user.id assert participation.conversation_id == conversation.id - # Needed because updated_at is accurate down to a second - :timer.sleep(1000) - # Creating again returns the same participation {:ok, %Participation{} = participation_two} = Participation.create_for_user_and_conversation(user, conversation) @@ -176,8 +175,8 @@ test "gets all the participations for a user, ordered by updated at descending" assert [participation_one, participation_two] = Participation.for_user(user) - object2 = Pleroma.Object.normalize(activity_two) - object3 = Pleroma.Object.normalize(activity_three) + object2 = Pleroma.Object.normalize(activity_two, fetch: false) + object3 = Pleroma.Object.normalize(activity_three, fetch: false) user = Repo.get(Pleroma.User, user.id) @@ -360,4 +359,16 @@ test "the conversation with the blocked user is not marked as unread on a reply" assert Participation.unread_count(blocked) == 1 end end + + test "deletes a conversation" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _activity} = + CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"}) + + assert [participation] = Participation.for_user(other_user) + assert {:ok, _} = Participation.delete(participation) + assert [] == Participation.for_user(other_user) + end end diff --git a/test/pleroma/conversation_test.exs b/test/pleroma/conversation_test.exs index 359aa6840..1a947606d 100644 --- a/test/pleroma/conversation_test.exs +++ b/test/pleroma/conversation_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ConversationTest do @@ -48,7 +48,7 @@ test "public posts don't create conversations" do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "Hey"}) - object = Pleroma.Object.normalize(activity) + object = Pleroma.Object.normalize(activity, fetch: false) context = object.data["context"] conversation = Conversation.get_for_ap_id(context) @@ -64,7 +64,7 @@ test "it creates or updates a conversation and participations for a given DM" do {:ok, activity} = CommonAPI.post(har, %{status: "Hey @#{jafnhar.nickname}", visibility: "direct"}) - object = Pleroma.Object.normalize(activity) + object = Pleroma.Object.normalize(activity, fetch: false) context = object.data["context"] conversation = @@ -86,7 +86,7 @@ test "it creates or updates a conversation and participations for a given DM" do in_reply_to_status_id: activity.id }) - object = Pleroma.Object.normalize(activity) + object = Pleroma.Object.normalize(activity, fetch: false) context = object.data["context"] conversation_two = @@ -110,7 +110,7 @@ test "it creates or updates a conversation and participations for a given DM" do in_reply_to_status_id: activity.id }) - object = Pleroma.Object.normalize(activity) + object = Pleroma.Object.normalize(activity, fetch: false) context = object.data["context"] conversation_three = diff --git a/test/pleroma/docs/generator_test.exs b/test/pleroma/docs/generator_test.exs index 43877244d..a9b09e577 100644 --- a/test/pleroma/docs/generator_test.exs +++ b/test/pleroma/docs/generator_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Docs.GeneratorTest do diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs index 812463454..259fd6a5f 100644 --- a/test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs +++ b/test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs @@ -1,10 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.DateTimeTest do alias Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime - use Pleroma.DataCase + use Pleroma.DataCase, async: true test "it validates an xsd:Datetime" do valid_strings = [ diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs index 732e2365f..1a4c2dfcb 100644 --- a/test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs +++ b/test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs @@ -1,10 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectIDTest do alias Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID - use Pleroma.DataCase + use Pleroma.DataCase, async: true @uris [ "http://lain.com/users/lain", diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs index 2e6a0c83d..d3a2fd13f 100644 --- a/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs +++ b/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs @@ -1,10 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.RecipientsTest do alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients - use Pleroma.DataCase + use Pleroma.DataCase, async: true test "it asserts that all elements of the list are object ids" do list = ["https://lain.com/users/lain", "invalid"] diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs index 7eddd2388..7002eca30 100644 --- a/test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs +++ b/test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.SafeTextTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText diff --git a/test/pleroma/emails/admin_email_test.exs b/test/pleroma/emails/admin_email_test.exs index 0da0699cc..04c907697 100644 --- a/test/pleroma/emails/admin_email_test.exs +++ b/test/pleroma/emails/admin_email_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emails.AdminEmailTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Emails.AdminEmail diff --git a/test/pleroma/emails/mailer_test.exs b/test/pleroma/emails/mailer_test.exs index 9e232d2a0..a8e1553e5 100644 --- a/test/pleroma/emails/mailer_test.exs +++ b/test/pleroma/emails/mailer_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emails.MailerTest do diff --git a/test/pleroma/emails/user_email_test.exs b/test/pleroma/emails/user_email_test.exs index a75623bb4..21fd06ea6 100644 --- a/test/pleroma/emails/user_email_test.exs +++ b/test/pleroma/emails/user_email_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emails.UserEmailTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Emails.UserEmail alias Pleroma.Web.Endpoint @@ -45,4 +45,15 @@ test "build account confirmation email" do assert email.html_body =~ Router.Helpers.confirm_email_url(Endpoint, :confirm_email, user.id, "conf-token") end + + test "build approval pending email" do + config = Pleroma.Config.get(:instance) + user = insert(:user) + email = UserEmail.approval_pending_email(user) + + assert email.from == {config[:name], config[:notify_email]} + assert email.to == [{user.name, user.email}] + assert email.subject == "Your account is awaiting approval" + assert email.html_body =~ "Awaiting Approval" + end end diff --git a/test/pleroma/emoji/formatter_test.exs b/test/pleroma/emoji/formatter_test.exs index 12af6cd8b..3942f609f 100644 --- a/test/pleroma/emoji/formatter_test.exs +++ b/test/pleroma/emoji/formatter_test.exs @@ -1,10 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emoji.FormatterTest do alias Pleroma.Emoji.Formatter - use Pleroma.DataCase + use Pleroma.DataCase, async: true describe "emojify" do test "it adds cool emoji" do diff --git a/test/pleroma/emoji/loader_test.exs b/test/pleroma/emoji/loader_test.exs index 804039e7e..de89e3bc4 100644 --- a/test/pleroma/emoji/loader_test.exs +++ b/test/pleroma/emoji/loader_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emoji.LoaderTest do diff --git a/test/pleroma/emoji/pack_test.exs b/test/pleroma/emoji/pack_test.exs index 70d1eaa1b..ac7535412 100644 --- a/test/pleroma/emoji/pack_test.exs +++ b/test/pleroma/emoji/pack_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Emoji.PackTest do - use ExUnit.Case, async: true + use Pleroma.DataCase alias Pleroma.Emoji.Pack @emoji_path Path.join( diff --git a/test/pleroma/emoji_test.exs b/test/pleroma/emoji_test.exs index 9cfd7b46b..027a8132f 100644 --- a/test/pleroma/emoji_test.exs +++ b/test/pleroma/emoji_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EmojiTest do - use ExUnit.Case + use ExUnit.Case, async: true alias Pleroma.Emoji describe "is_unicode_emoji?/1" do diff --git a/test/pleroma/filter_test.exs b/test/pleroma/filter_test.exs index 0a5c4426a..19ad6b8c0 100644 --- a/test/pleroma/filter_test.exs +++ b/test/pleroma/filter_test.exs @@ -1,87 +1,126 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.FilterTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory + alias Oban.Job alias Pleroma.Filter - alias Pleroma.Repo + + setup do + [user: insert(:user)] + end describe "creating filters" do - test "creating one filter" do - user = insert(:user) + test "creation validation error", %{user: user} do + attrs = %{ + user_id: user.id, + expires_in: 60 + } - query = %Filter{ + {:error, _} = Filter.create(attrs) + + assert Repo.all(Job) == [] + end + + test "use passed expires_at instead expires_in", %{user: user} do + now = NaiveDateTime.utc_now() + + attrs = %{ + user_id: user.id, + expires_at: now, + phrase: "knights", + context: ["home"], + expires_in: 600 + } + + {:ok, %Filter{} = filter} = Filter.create(attrs) + + result = Filter.get(filter.filter_id, user) + assert result.expires_at == NaiveDateTime.truncate(now, :second) + + [job] = Repo.all(Job) + + assert DateTime.truncate(job.scheduled_at, :second) == + now |> NaiveDateTime.truncate(:second) |> DateTime.from_naive!("Etc/UTC") + end + + test "creating one filter", %{user: user} do + attrs = %{ user_id: user.id, filter_id: 42, phrase: "knights", context: ["home"] } - {:ok, %Filter{} = filter} = Filter.create(query) + {:ok, %Filter{} = filter} = Filter.create(attrs) result = Filter.get(filter.filter_id, user) - assert query.phrase == result.phrase + assert attrs.phrase == result.phrase end - test "creating one filter without a pre-defined filter_id" do - user = insert(:user) + test "creating with expired_at", %{user: user} do + attrs = %{ + user_id: user.id, + filter_id: 42, + phrase: "knights", + context: ["home"], + expires_in: 60 + } - query = %Filter{ + {:ok, %Filter{} = filter} = Filter.create(attrs) + result = Filter.get(filter.filter_id, user) + assert attrs.phrase == result.phrase + + assert [_] = Repo.all(Job) + end + + test "creating one filter without a pre-defined filter_id", %{user: user} do + attrs = %{ user_id: user.id, phrase: "knights", context: ["home"] } - {:ok, %Filter{} = filter} = Filter.create(query) + {:ok, %Filter{} = filter} = Filter.create(attrs) # Should start at 1 assert filter.filter_id == 1 end - test "creating additional filters uses previous highest filter_id + 1" do - user = insert(:user) + test "creating additional filters uses previous highest filter_id + 1", %{user: user} do + filter1 = insert(:filter, user: user) - query_one = %Filter{ - user_id: user.id, - filter_id: 42, - phrase: "knights", - context: ["home"] - } - - {:ok, %Filter{} = filter_one} = Filter.create(query_one) - - query_two = %Filter{ + attrs = %{ user_id: user.id, # No filter_id phrase: "who", context: ["home"] } - {:ok, %Filter{} = filter_two} = Filter.create(query_two) - assert filter_two.filter_id == filter_one.filter_id + 1 + {:ok, %Filter{} = filter2} = Filter.create(attrs) + assert filter2.filter_id == filter1.filter_id + 1 end - test "filter_id is unique per user" do - user_one = insert(:user) + test "filter_id is unique per user", %{user: user_one} do user_two = insert(:user) - query_one = %Filter{ + attrs1 = %{ user_id: user_one.id, phrase: "knights", context: ["home"] } - {:ok, %Filter{} = filter_one} = Filter.create(query_one) + {:ok, %Filter{} = filter_one} = Filter.create(attrs1) - query_two = %Filter{ + attrs2 = %{ user_id: user_two.id, phrase: "who", context: ["home"] } - {:ok, %Filter{} = filter_two} = Filter.create(query_two) + {:ok, %Filter{} = filter_two} = Filter.create(attrs2) assert filter_one.filter_id == 1 assert filter_two.filter_id == 1 @@ -94,65 +133,61 @@ test "filter_id is unique per user" do end end - test "deleting a filter" do - user = insert(:user) + test "deleting a filter", %{user: user} do + filter = insert(:filter, user: user) - query = %Filter{ - user_id: user.id, - filter_id: 0, - phrase: "knights", - context: ["home"] - } - - {:ok, _filter} = Filter.create(query) - {:ok, filter} = Filter.delete(query) - assert is_nil(Repo.get(Filter, filter.filter_id)) + assert Repo.get(Filter, filter.id) + {:ok, filter} = Filter.delete(filter) + refute Repo.get(Filter, filter.id) end - test "getting all filters by an user" do - user = insert(:user) - - query_one = %Filter{ + test "deleting a filter with expires_at is removing Oban job too", %{user: user} do + attrs = %{ user_id: user.id, - filter_id: 1, - phrase: "knights", - context: ["home"] + phrase: "cofe", + context: ["home"], + expires_in: 600 } - query_two = %Filter{ - user_id: user.id, - filter_id: 2, - phrase: "who", - context: ["home"] - } + {:ok, filter} = Filter.create(attrs) + assert %Job{id: job_id} = Pleroma.Workers.PurgeExpiredFilter.get_expiration(filter.id) + {:ok, _} = Filter.delete(filter) - {:ok, filter_one} = Filter.create(query_one) - {:ok, filter_two} = Filter.create(query_two) - filters = Filter.get_filters(user) - assert filter_one in filters - assert filter_two in filters + assert Repo.get(Job, job_id) == nil end - test "updating a filter" do - user = insert(:user) + test "getting all filters by an user", %{user: user} do + filter1 = insert(:filter, user: user) + filter2 = insert(:filter, user: user) - query_one = %Filter{ - user_id: user.id, - filter_id: 1, - phrase: "knights", - context: ["home"] - } + filter_ids = user |> Filter.get_filters() |> collect_ids() + + assert filter1.id in filter_ids + assert filter2.id in filter_ids + end + + test "updating a filter", %{user: user} do + filter = insert(:filter, user: user) changes = %{ phrase: "who", context: ["home", "timeline"] } - {:ok, filter_one} = Filter.create(query_one) - {:ok, filter_two} = Filter.update(filter_one, changes) + {:ok, updated_filter} = Filter.update(filter, changes) - assert filter_one != filter_two - assert filter_two.phrase == changes.phrase - assert filter_two.context == changes.context + assert filter != updated_filter + assert updated_filter.phrase == changes.phrase + assert updated_filter.context == changes.context + end + + test "updating with error", %{user: user} do + filter = insert(:filter, user: user) + + changes = %{ + phrase: nil + } + + {:error, _} = Filter.update(filter, changes) end end diff --git a/test/pleroma/following_relationship_test.exs b/test/pleroma/following_relationship_test.exs index 17a468abb..37452996b 100644 --- a/test/pleroma/following_relationship_test.exs +++ b/test/pleroma/following_relationship_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.FollowingRelationshipTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.FollowingRelationship alias Pleroma.Web.ActivityPub.InternalFetchActor diff --git a/test/pleroma/formatter_test.exs b/test/pleroma/formatter_test.exs index 5781a3f01..7f54638fb 100644 --- a/test/pleroma/formatter_test.exs +++ b/test/pleroma/formatter_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.FormatterTest do diff --git a/test/pleroma/frontend_test.exs b/test/pleroma/frontend_test.exs index 223625857..1b50a031d 100644 --- a/test/pleroma/frontend_test.exs +++ b/test/pleroma/frontend_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.FrontendTest do diff --git a/test/pleroma/gun/connection_pool_test.exs b/test/pleroma/gun/connection_pool_test.exs index aea908fac..4b3158625 100644 --- a/test/pleroma/gun/connection_pool_test.exs +++ b/test/pleroma/gun/connection_pool_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Gun.ConnectionPoolTest do @@ -7,7 +7,6 @@ defmodule Pleroma.Gun.ConnectionPoolTest do import Mox import ExUnit.CaptureLog - alias Pleroma.Config alias Pleroma.Gun.ConnectionPool defp gun_mock(_) do @@ -19,7 +18,6 @@ defp gun_mock(_) do :ok end - setup :set_mox_from_context setup :gun_mock test "gives the same connection to 2 concurrent requests" do @@ -50,7 +48,7 @@ test "gives the same connection to 2 concurrent requests" do test "connection limit is respected with concurrent requests" do clear_config([:connections_pool, :max_connections]) do - Config.put([:connections_pool, :max_connections], 1) + clear_config([:connections_pool, :max_connections], 1) # The supervisor needs a reboot to apply the new config setting Process.exit(Process.whereis(Pleroma.Gun.ConnectionPool.WorkerSupervisor), :kill) diff --git a/test/pleroma/hashtag_test.exs b/test/pleroma/hashtag_test.exs new file mode 100644 index 000000000..0264dea0b --- /dev/null +++ b/test/pleroma/hashtag_test.exs @@ -0,0 +1,17 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HashtagTest do + use Pleroma.DataCase + + alias Pleroma.Hashtag + + describe "changeset validations" do + test "ensure non-blank :name" do + changeset = Hashtag.changeset(%Hashtag{}, %{name: ""}) + + assert {:name, {"can't be blank", [validation: :required]}} in changeset.errors + end + end +end diff --git a/test/pleroma/healthcheck_test.exs b/test/pleroma/healthcheck_test.exs index e341e6983..469e5b397 100644 --- a/test/pleroma/healthcheck_test.exs +++ b/test/pleroma/healthcheck_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HealthcheckTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Healthcheck test "system_info/0" do diff --git a/test/pleroma/html_test.exs b/test/pleroma/html_test.exs index 7d3756884..fe1a1ed57 100644 --- a/test/pleroma/html_test.exs +++ b/test/pleroma/html_test.exs @@ -1,12 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTMLTest do alias Pleroma.HTML alias Pleroma.Object alias Pleroma.Web.CommonAPI - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory @@ -175,7 +175,7 @@ test "extracts the url" do "I think I just found the best github repo https://github.com/komeiji-satori/Dress" }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) {:ok, url} = HTML.extract_first_external_url_from_object(object) assert url == "https://github.com/komeiji-satori/Dress" end @@ -190,7 +190,7 @@ test "skips mentions" do "@#{other_user.nickname} install misskey! https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md" }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) {:ok, url} = HTML.extract_first_external_url_from_object(object) assert url == "https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md" @@ -206,7 +206,7 @@ test "skips hashtags" do status: "#cofe https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) {:ok, url} = HTML.extract_first_external_url_from_object(object) assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" @@ -222,7 +222,7 @@ test "skips microformats hashtags" do content_type: "text/html" }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) {:ok, url} = HTML.extract_first_external_url_from_object(object) assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" @@ -233,7 +233,7 @@ test "does not crash when there is an HTML entity in a link" do {:ok, activity} = CommonAPI.post(user, %{status: "\"http://cofe.com/?boomer=ok&foo=bar\""}) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert {:ok, nil} = HTML.extract_first_external_url_from_object(object) end @@ -247,7 +247,7 @@ test "skips attachment links" do "image.png" }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert {:ok, nil} = HTML.extract_first_external_url_from_object(object) end diff --git a/test/pleroma/http/adapter_helper/gun_test.exs b/test/pleroma/http/adapter_helper/gun_test.exs index 80589c73d..cfb68557d 100644 --- a/test/pleroma/http/adapter_helper/gun_test.exs +++ b/test/pleroma/http/adapter_helper/gun_test.exs @@ -1,14 +1,13 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.AdapterHelper.GunTest do - use ExUnit.Case, async: true + use ExUnit.Case use Pleroma.Tests.Helpers import Mox - alias Pleroma.Config alias Pleroma.HTTP.AdapterHelper.Gun setup :verify_on_exit! @@ -52,9 +51,7 @@ test "merges with defaul http adapter config" do end test "parses string proxy host & port" do - proxy = Config.get([:http, :proxy_url]) - Config.put([:http, :proxy_url], "localhost:8123") - on_exit(fn -> Config.put([:http, :proxy_url], proxy) end) + clear_config([:http, :proxy_url], "localhost:8123") uri = URI.parse("https://some-domain.com") opts = Gun.options([receive_conn: false], uri) @@ -62,9 +59,7 @@ test "parses string proxy host & port" do end test "parses tuple proxy scheme host and port" do - proxy = Config.get([:http, :proxy_url]) - Config.put([:http, :proxy_url], {:socks, 'localhost', 1234}) - on_exit(fn -> Config.put([:http, :proxy_url], proxy) end) + clear_config([:http, :proxy_url], {:socks, 'localhost', 1234}) uri = URI.parse("https://some-domain.com") opts = Gun.options([receive_conn: false], uri) @@ -72,9 +67,7 @@ test "parses tuple proxy scheme host and port" do end test "passed opts have more weight than defaults" do - proxy = Config.get([:http, :proxy_url]) - Config.put([:http, :proxy_url], {:socks5, 'localhost', 1234}) - on_exit(fn -> Config.put([:http, :proxy_url], proxy) end) + clear_config([:http, :proxy_url], {:socks5, 'localhost', 1234}) uri = URI.parse("https://some-domain.com") opts = Gun.options([receive_conn: false, proxy: {'example.com', 4321}], uri) diff --git a/test/pleroma/http/adapter_helper/hackney_test.exs b/test/pleroma/http/adapter_helper/hackney_test.exs index f2361ff0b..85150a65c 100644 --- a/test/pleroma/http/adapter_helper/hackney_test.exs +++ b/test/pleroma/http/adapter_helper/hackney_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.AdapterHelper.HackneyTest do diff --git a/test/pleroma/http/adapter_helper_test.exs b/test/pleroma/http/adapter_helper_test.exs index 24d501ad5..3c8c89164 100644 --- a/test/pleroma/http/adapter_helper_test.exs +++ b/test/pleroma/http/adapter_helper_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.AdapterHelperTest do diff --git a/test/pleroma/http/ex_aws_test.exs b/test/pleroma/http/ex_aws_test.exs index d0b00ca26..4cbc440bd 100644 --- a/test/pleroma/http/ex_aws_test.exs +++ b/test/pleroma/http/ex_aws_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.ExAwsTest do diff --git a/test/pleroma/http/request_builder_test.exs b/test/pleroma/http/request_builder_test.exs index fab909905..e9b0c4a8a 100644 --- a/test/pleroma/http/request_builder_test.exs +++ b/test/pleroma/http/request_builder_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.RequestBuilderTest do diff --git a/test/pleroma/http/tzdata_test.exs b/test/pleroma/http/tzdata_test.exs index 3e605d33b..1161bfaef 100644 --- a/test/pleroma/http/tzdata_test.exs +++ b/test/pleroma/http/tzdata_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTP.TzdataTest do diff --git a/test/pleroma/http_test.exs b/test/pleroma/http_test.exs index d394bb942..e6a6d31b3 100644 --- a/test/pleroma/http_test.exs +++ b/test/pleroma/http_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.HTTPTest do diff --git a/test/pleroma/instances/instance_test.exs b/test/pleroma/instances/instance_test.exs index 2c6389e4f..bacc0b19b 100644 --- a/test/pleroma/instances/instance_test.exs +++ b/test/pleroma/instances/instance_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Instances.InstanceTest do diff --git a/test/pleroma/instances_test.exs b/test/pleroma/instances_test.exs index 5d0ce6237..03f9e4e97 100644 --- a/test/pleroma/instances_test.exs +++ b/test/pleroma/instances_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.InstancesTest do diff --git a/test/pleroma/integration/federation_test.exs b/test/pleroma/integration/federation_test.exs index 10d71fb88..da433e2c0 100644 --- a/test/pleroma/integration/federation_test.exs +++ b/test/pleroma/integration/federation_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Integration.FederationTest do diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs index bb8e795b7..43ec57893 100644 --- a/test/pleroma/integration/mastodon_websocket_test.exs +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -1,8 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Integration.MastodonWebsocketTest do + # Needs a streamer, needs to stay synchronous use Pleroma.DataCase import ExUnit.CaptureLog diff --git a/test/pleroma/job_queue_monitor_test.exs b/test/pleroma/job_queue_monitor_test.exs index 65c1e9f29..eebf602c5 100644 --- a/test/pleroma/job_queue_monitor_test.exs +++ b/test/pleroma/job_queue_monitor_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.JobQueueMonitorTest do diff --git a/test/pleroma/keys_test.exs b/test/pleroma/keys_test.exs index 9e8528cba..9a15bf06e 100644 --- a/test/pleroma/keys_test.exs +++ b/test/pleroma/keys_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.KeysTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Keys diff --git a/test/pleroma/list_test.exs b/test/pleroma/list_test.exs index b5572cbae..7e66ad385 100644 --- a/test/pleroma/list_test.exs +++ b/test/pleroma/list_test.exs @@ -1,10 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ListTest do alias Pleroma.Repo - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory diff --git a/test/pleroma/marker_test.exs b/test/pleroma/marker_test.exs index 7b3943c7b..5f87a1c38 100644 --- a/test/pleroma/marker_test.exs +++ b/test/pleroma/marker_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MarkerTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Marker import Pleroma.Factory diff --git a/test/pleroma/mfa/backup_codes_test.exs b/test/pleroma/mfa/backup_codes_test.exs index 41adb1e96..59f984e32 100644 --- a/test/pleroma/mfa/backup_codes_test.exs +++ b/test/pleroma/mfa/backup_codes_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.BackupCodesTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.MFA.BackupCodes diff --git a/test/pleroma/mfa/totp_test.exs b/test/pleroma/mfa/totp_test.exs index 9edb6fd54..828993866 100644 --- a/test/pleroma/mfa/totp_test.exs +++ b/test/pleroma/mfa/totp_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFA.TOTPTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.MFA.TOTP diff --git a/test/pleroma/mfa_test.exs b/test/pleroma/mfa_test.exs index 8875cefd9..76ba1a99d 100644 --- a/test/pleroma/mfa_test.exs +++ b/test/pleroma/mfa_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2018 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MFATest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.MFA @@ -30,8 +30,8 @@ test "returns backup codes" do {:ok, [code1, code2]} = MFA.generate_backup_codes(user) updated_user = refresh_record(user) [hash1, hash2] = updated_user.multi_factor_authentication_settings.backup_codes - assert Pbkdf2.verify_pass(code1, hash1) - assert Pbkdf2.verify_pass(code2, hash2) + assert Pleroma.Password.Pbkdf2.verify_pass(code1, hash1) + assert Pleroma.Password.Pbkdf2.verify_pass(code2, hash2) end end diff --git a/test/pleroma/migration_helper/notification_backfill_test.exs b/test/pleroma/migration_helper/notification_backfill_test.exs index 2a62a2b00..fd253b530 100644 --- a/test/pleroma/migration_helper/notification_backfill_test.exs +++ b/test/pleroma/migration_helper/notification_backfill_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.MigrationHelper.NotificationBackfillTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Activity alias Pleroma.MigrationHelper.NotificationBackfill diff --git a/test/pleroma/moderation_log_test.exs b/test/pleroma/moderation_log_test.exs index 03b32a060..c6c170c45 100644 --- a/test/pleroma/moderation_log_test.exs +++ b/test/pleroma/moderation_log_test.exs @@ -1,12 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ModerationLogTest do alias Pleroma.Activity alias Pleroma.ModerationLog - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory diff --git a/test/pleroma/notification_test.exs b/test/pleroma/notification_test.exs index a6558f995..abf1b0410 100644 --- a/test/pleroma/notification_test.exs +++ b/test/pleroma/notification_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.NotificationTest do @@ -45,6 +45,20 @@ test "creates a notification for a report" do assert notification.type == "pleroma:report" end + test "suppresses notification to reporter if reporter is an admin" do + reporting_admin = insert(:user, is_admin: true) + reported_user = insert(:user) + other_admin = insert(:user, is_admin: true) + + {:ok, activity} = CommonAPI.report(reporting_admin, %{account_id: reported_user.id}) + + {:ok, [notification]} = Notification.create_notifications(activity) + + refute notification.user_id == reporting_admin.id + assert notification.user_id == other_admin.id + assert notification.type == "pleroma:report" + end + test "creates a notification for an emoji reaction" do user = insert(:user) other_user = insert(:user) @@ -976,7 +990,6 @@ test "notifications are deleted if a remote user is deleted" do assert Enum.empty?(Notification.for_user(local_user)) end - @tag capture_log: true test "move activity generates a notification" do %{ap_id: old_ap_id} = old_user = insert(:user) %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id]) @@ -986,18 +999,6 @@ test "move activity generates a notification" do User.follow(follower, old_user) User.follow(other_follower, old_user) - old_user_url = old_user.ap_id - - body = - File.read!("test/fixtures/users_mock/localhost.json") - |> String.replace("{{nickname}}", old_user.nickname) - |> Jason.encode!() - - Tesla.Mock.mock(fn - %{method: :get, url: ^old_user_url} -> - %Tesla.Env{status: 200, body: body} - end) - Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) ObanHelpers.perform_all() diff --git a/test/pleroma/object/containment_test.exs b/test/pleroma/object/containment_test.exs index 90b6dccf2..fb2fb7d49 100644 --- a/test/pleroma/object/containment_test.exs +++ b/test/pleroma/object/containment_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Object.ContainmentTest do diff --git a/test/pleroma/object/fetcher_test.exs b/test/pleroma/object/fetcher_test.exs index 7df6af7fe..a7ac90348 100644 --- a/test/pleroma/object/fetcher_test.exs +++ b/test/pleroma/object/fetcher_test.exs @@ -1,12 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Object.FetcherTest do use Pleroma.DataCase alias Pleroma.Activity - alias Pleroma.Config alias Pleroma.Object alias Pleroma.Object.Fetcher @@ -87,20 +86,20 @@ test "it works when fetching the OP actor errors out" do setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) test "it returns thread depth exceeded error if thread depth is exceeded" do - Config.put([:instance, :federation_incoming_replies_max_depth], 0) + clear_config([:instance, :federation_incoming_replies_max_depth], 0) assert {:error, "Max thread distance exceeded."} = Fetcher.fetch_object_from_id(@ap_id, depth: 1) end test "it fetches object if max thread depth is restricted to 0 and depth is not specified" do - Config.put([:instance, :federation_incoming_replies_max_depth], 0) + clear_config([:instance, :federation_incoming_replies_max_depth], 0) assert {:ok, _} = Fetcher.fetch_object_from_id(@ap_id) end test "it fetches object if requested depth does not exceed max thread depth" do - Config.put([:instance, :federation_incoming_replies_max_depth], 10) + clear_config([:instance, :federation_incoming_replies_max_depth], 10) assert {:ok, _} = Fetcher.fetch_object_from_id(@ap_id, depth: 10) end @@ -245,7 +244,7 @@ test "it can refetch pruned objects" do Pleroma.Signature, [:passthrough], [] do - Config.put([:activitypub, :sign_object_fetches], true) + clear_config([:activitypub, :sign_object_fetches], true) Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") @@ -256,7 +255,7 @@ test "it can refetch pruned objects" do Pleroma.Signature, [:passthrough], [] do - Config.put([:activitypub, :sign_object_fetches], false) + clear_config([:activitypub, :sign_object_fetches], false) Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") diff --git a/test/pleroma/object_test.exs b/test/pleroma/object_test.exs index 5d4e6fb84..8320660a5 100644 --- a/test/pleroma/object_test.exs +++ b/test/pleroma/object_test.exs @@ -1,14 +1,17 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only 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.Hashtag alias Pleroma.Object alias Pleroma.Repo alias Pleroma.Tests.ObanHelpers @@ -78,8 +81,8 @@ test "ensures cache is cleared for the object" do setup do: clear_config([:instance, :cleanup_attachments]) test "Disabled via config" do - Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) - Pleroma.Config.put([:instance, :cleanup_attachments], false) + clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + clear_config([:instance, :cleanup_attachments], false) file = %Plug.Upload{ content_type: "image/jpeg", @@ -112,8 +115,8 @@ test "Disabled via config" do end test "in subdirectories" do - Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) - Pleroma.Config.put([:instance, :cleanup_attachments], true) + clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + clear_config([:instance, :cleanup_attachments], true) file = %Plug.Upload{ content_type: "image/jpeg", @@ -146,9 +149,9 @@ test "in subdirectories" do end test "with dedupe enabled" do - Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) - Pleroma.Config.put([Pleroma.Upload, :filters], [Pleroma.Upload.Filter.Dedupe]) - Pleroma.Config.put([:instance, :cleanup_attachments], true) + clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + clear_config([Pleroma.Upload, :filters], [Pleroma.Upload.Filter.Dedupe]) + clear_config([:instance, :cleanup_attachments], true) uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads]) @@ -184,8 +187,8 @@ test "with dedupe enabled" do end test "with objects that have legacy data.url attribute" do - Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) - Pleroma.Config.put([:instance, :cleanup_attachments], true) + clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + clear_config([:instance, :cleanup_attachments], true) file = %Plug.Upload{ content_type: "image/jpeg", @@ -220,9 +223,9 @@ test "with objects that have legacy data.url attribute" do end test "With custom base_url" do - Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) - Pleroma.Config.put([Pleroma.Upload, :base_url], "https://sub.domain.tld/dir/") - Pleroma.Config.put([:instance, :cleanup_attachments], true) + clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + clear_config([Pleroma.Upload, :base_url], "https://sub.domain.tld/dir/") + clear_config([:instance, :cleanup_attachments], true) file = %Plug.Upload{ content_type: "image/jpeg", @@ -256,23 +259,22 @@ test "With custom base_url" do end describe "normalizer" do - test "fetches unknown objects by default" do - %Object{} = - object = Object.normalize("http://mastodon.example.org/@admin/99541947525187367") - - assert object.data["url"] == "http://mastodon.example.org/@admin/99541947525187367" + @url "http://mastodon.example.org/@admin/99541947525187367" + test "does not fetch unknown objects by default" do + assert nil == Object.normalize(@url) end - test "fetches unknown objects when fetch_remote is explicitly true" do - %Object{} = - object = Object.normalize("http://mastodon.example.org/@admin/99541947525187367", true) + test "fetches unknown objects when fetch is explicitly true" do + %Object{} = object = Object.normalize(@url, fetch: true) - assert object.data["url"] == "http://mastodon.example.org/@admin/99541947525187367" + assert object.data["url"] == @url end - test "does not fetch unknown objects when fetch_remote is false" do + test "does not fetch unknown objects when fetch is false" do assert is_nil( - Object.normalize("http://mastodon.example.org/@admin/99541947525187367", false) + Object.normalize(@url, + fetch: false + ) ) end end @@ -310,7 +312,10 @@ test "refetches if the time since the last refetch is greater than the interval" mock_modified: mock_modified } do %Object{} = - object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d") + object = + Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d", + fetch: true + ) Object.set_cache(object) @@ -332,7 +337,10 @@ test "refetches if the time since the last refetch is greater than the interval" test "returns the old object if refetch fails", %{mock_modified: mock_modified} do %Object{} = - object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d") + object = + Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d", + fetch: true + ) Object.set_cache(object) @@ -355,7 +363,10 @@ test "does not refetch if the time since the last refetch is greater than the in mock_modified: mock_modified } do %Object{} = - object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d") + object = + Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d", + fetch: true + ) Object.set_cache(object) @@ -377,7 +388,10 @@ test "does not refetch if the time since the last refetch is greater than the in test "preserves internal fields on refetch", %{mock_modified: mock_modified} do %Object{} = - object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d") + object = + Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d", + fetch: true + ) Object.set_cache(object) @@ -406,4 +420,28 @@ test "preserves internal fields on refetch", %{mock_modified: mock_modified} do assert updated_object.data["like_count"] == 1 end end + + describe ":hashtags association" do + test "Hashtag records are created with Object record and updated on its change" do + user = insert(:user) + + {:ok, %{object: object}} = + CommonAPI.post(user, %{status: "some text #hashtag1 #hashtag2 ..."}) + + assert [%Hashtag{name: "hashtag1"}, %Hashtag{name: "hashtag2"}] = + Enum.sort_by(object.hashtags, & &1.name) + + {:ok, object} = Object.update_data(object, %{"tag" => []}) + + assert [] = object.hashtags + + object = Object.get_by_id(object.id) |> Repo.preload(:hashtags) + assert [] = object.hashtags + + {:ok, object} = Object.update_data(object, %{"tag" => ["abc", "def"]}) + + assert [%Hashtag{name: "abc"}, %Hashtag{name: "def"}] = + Enum.sort_by(object.hashtags, & &1.name) + end + end end diff --git a/test/pleroma/otp_version_test.exs b/test/pleroma/otp_version_test.exs index 7d2538ec8..736d440af 100644 --- a/test/pleroma/otp_version_test.exs +++ b/test/pleroma/otp_version_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.OTPVersionTest do diff --git a/test/pleroma/pagination_test.exs b/test/pleroma/pagination_test.exs index e526f23e8..bc26c8b46 100644 --- a/test/pleroma/pagination_test.exs +++ b/test/pleroma/pagination_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.PaginationTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory diff --git a/test/pleroma/password/pbkdf2_test.exs b/test/pleroma/password/pbkdf2_test.exs new file mode 100644 index 000000000..e55348f9a --- /dev/null +++ b/test/pleroma/password/pbkdf2_test.exs @@ -0,0 +1,35 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Password.Pbkdf2Test do + use Pleroma.DataCase, async: true + + alias Pleroma.Password.Pbkdf2, as: Password + + test "it generates the same hash as pbkd2_elixir" do + # hash = Pbkdf2.hash_pwd_salt("password") + hash = + "$pbkdf2-sha512$1$QJpEYw8iBKcnY.4Rm0eCVw$UBPeWQ91RxSv3snxsb/ZzMeG/2aa03c541bbo8vQudREGNta5t8jBQrd00fyJp8RjaqfvgdZxy2rhSwljyu21g" + + # Use the same randomly generated salt + salt = Password.decode64("QJpEYw8iBKcnY.4Rm0eCVw") + + assert hash == Password.hash_pwd_salt("password", salt: salt) + end + + @tag skip: "Works when Pbkd2 is present. Source: trust me bro" + test "Pbkdf2 can verify passwords generated with it" do + # Commented to prevent warnings. + # hash = Password.hash_pwd_salt("password") + # assert Pbkdf2.verify_pass("password", hash) + end + + test "it verifies pbkdf2_elixir hashes" do + # hash = Pbkdf2.hash_pwd_salt("password") + hash = + "$pbkdf2-sha512$1$QJpEYw8iBKcnY.4Rm0eCVw$UBPeWQ91RxSv3snxsb/ZzMeG/2aa03c541bbo8vQudREGNta5t8jBQrd00fyJp8RjaqfvgdZxy2rhSwljyu21g" + + assert Password.verify_pass("password", hash) + end +end diff --git a/test/pleroma/registration_test.exs b/test/pleroma/registration_test.exs index 7db8e3664..6e4ad9487 100644 --- a/test/pleroma/registration_test.exs +++ b/test/pleroma/registration_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.RegistrationTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory diff --git a/test/pleroma/repo/migrations/autolinker_to_linkify_test.exs b/test/pleroma/repo/migrations/autolinker_to_linkify_test.exs index 84f520fe4..a7d4d493c 100644 --- a/test/pleroma/repo/migrations/autolinker_to_linkify_test.exs +++ b/test/pleroma/repo/migrations/autolinker_to_linkify_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Repo.Migrations.AutolinkerToLinkifyTest do @@ -37,7 +37,7 @@ test "change/0 converts auto_linker opts for Pleroma.Formatter", %{migration: mi strip_prefix: false ] - Pleroma.Config.put(Pleroma.Formatter, new_opts) + clear_config(Pleroma.Formatter, new_opts) assert new_opts == Pleroma.Config.get(Pleroma.Formatter) {text, _mentions, []} = diff --git a/test/pleroma/repo/migrations/confirm_logged_in_users_test.exs b/test/pleroma/repo/migrations/confirm_logged_in_users_test.exs new file mode 100644 index 000000000..99d17f62a --- /dev/null +++ b/test/pleroma/repo/migrations/confirm_logged_in_users_test.exs @@ -0,0 +1,40 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.ConfirmLoggedInUsersTest do + alias Pleroma.Repo + alias Pleroma.User + use Pleroma.DataCase, async: true + import Ecto.Query + import Pleroma.Factory + import Pleroma.Tests.Helpers + + setup_all do: require_migration("20201231185546_confirm_logged_in_users") + + test "up/0 confirms unconfirmed but previously-logged-in users", %{migration: migration} do + insert_list(25, :oauth_token) + Repo.update_all(User, set: [is_confirmed: false]) + insert_list(5, :user, is_confirmed: false) + + count = + User + |> where(is_confirmed: false) + |> Repo.aggregate(:count) + + assert count == 30 + + assert {25, nil} == migration.up() + + count = + User + |> where(is_confirmed: false) + |> Repo.aggregate(:count) + + assert count == 5 + end + + test "down/0 does nothing", %{migration: migration} do + assert :noop == migration.down() + end +end diff --git a/test/pleroma/repo/migrations/deprecate_public_endpoint_test.exs b/test/pleroma/repo/migrations/deprecate_public_endpoint_test.exs new file mode 100644 index 000000000..2ffc1b145 --- /dev/null +++ b/test/pleroma/repo/migrations/deprecate_public_endpoint_test.exs @@ -0,0 +1,60 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.DeprecatePublicEndpointTest do + use Pleroma.DataCase + import Pleroma.Factory + import Pleroma.Tests.Helpers + alias Pleroma.ConfigDB + + setup do: clear_config(Pleroma.Upload) + setup do: clear_config(Pleroma.Uploaders.S3) + setup_all do: require_migration("20210113225652_deprecate_public_endpoint") + + test "up/0 migrates public_endpoint to base_url", %{migration: migration} do + s3_values = [ + public_endpoint: "https://coolhost.com/", + bucket: "secret_bucket" + ] + + insert(:config, group: :pleroma, key: Pleroma.Uploaders.S3, value: s3_values) + + upload_values = [ + uploader: Pleroma.Uploaders.S3 + ] + + insert(:config, group: :pleroma, key: Pleroma.Upload, value: upload_values) + + migration.up() + + assert [bucket: "secret_bucket"] == + ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Uploaders.S3}).value + + assert [uploader: Pleroma.Uploaders.S3, base_url: "https://coolhost.com/"] == + ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Upload}).value + end + + test "down/0 reverts base_url to public_endpoint", %{migration: migration} do + s3_values = [ + bucket: "secret_bucket" + ] + + insert(:config, group: :pleroma, key: Pleroma.Uploaders.S3, value: s3_values) + + upload_values = [ + uploader: Pleroma.Uploaders.S3, + base_url: "https://coolhost.com/" + ] + + insert(:config, group: :pleroma, key: Pleroma.Upload, value: upload_values) + + migration.down() + + assert [bucket: "secret_bucket", public_endpoint: "https://coolhost.com/"] == + ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Uploaders.S3}).value + + assert [uploader: Pleroma.Uploaders.S3] == + ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Upload}).value + end +end diff --git a/test/pleroma/repo/migrations/fix_legacy_tags_test.exs b/test/pleroma/repo/migrations/fix_legacy_tags_test.exs index 432055e45..0a1d1d0bb 100644 --- a/test/pleroma/repo/migrations/fix_legacy_tags_test.exs +++ b/test/pleroma/repo/migrations/fix_legacy_tags_test.exs @@ -1,10 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Repo.Migrations.FixLegacyTagsTest do alias Pleroma.User - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory import Pleroma.Tests.Helpers diff --git a/test/pleroma/repo/migrations/fix_malformed_formatter_config_test.exs b/test/pleroma/repo/migrations/fix_malformed_formatter_config_test.exs index 61528599a..65c9961b0 100644 --- a/test/pleroma/repo/migrations/fix_malformed_formatter_config_test.exs +++ b/test/pleroma/repo/migrations/fix_malformed_formatter_config_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Repo.Migrations.FixMalformedFormatterConfigTest do @@ -34,7 +34,7 @@ test "change/0 converts a map into a list", %{migration: migration} do strip_prefix: false ] - Pleroma.Config.put(Pleroma.Formatter, new_opts) + clear_config(Pleroma.Formatter, new_opts) assert new_opts == Pleroma.Config.get(Pleroma.Formatter) {text, _mentions, []} = diff --git a/test/pleroma/repo/migrations/move_welcome_settings_test.exs b/test/pleroma/repo/migrations/move_welcome_settings_test.exs index 53d05a55a..1da6b8a04 100644 --- a/test/pleroma/repo/migrations/move_welcome_settings_test.exs +++ b/test/pleroma/repo/migrations/move_welcome_settings_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Repo.Migrations.MoveWelcomeSettingsTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory import Pleroma.Tests.Helpers alias Pleroma.ConfigDB diff --git a/test/pleroma/repo_test.exs b/test/pleroma/repo_test.exs index 155791be2..9e14bdbd1 100644 --- a/test/pleroma/repo_test.exs +++ b/test/pleroma/repo_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.RepoTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.User diff --git a/test/pleroma/report_note_test.exs b/test/pleroma/report_note_test.exs index 25c1d6a61..2620560a0 100644 --- a/test/pleroma/report_note_test.exs +++ b/test/pleroma/report_note_test.exs @@ -1,10 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReportNoteTest do alias Pleroma.ReportNote - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory test "create/3" do diff --git a/test/pleroma/reverse_proxy_test.exs b/test/pleroma/reverse_proxy_test.exs index 8df63de65..a4dd8e99a 100644 --- a/test/pleroma/reverse_proxy_test.exs +++ b/test/pleroma/reverse_proxy_test.exs @@ -1,10 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ReverseProxyTest do - use Pleroma.Web.ConnCase, async: true - + use Pleroma.Web.ConnCase import ExUnit.CaptureLog import Mox @@ -19,24 +18,23 @@ defmodule Pleroma.ReverseProxyTest do setup :verify_on_exit! - defp user_agent_mock(user_agent, invokes) do - json = Jason.encode!(%{"user-agent": user_agent}) - + defp request_mock(invokes) do ClientMock - |> expect(:request, fn :get, url, _, _, _ -> + |> expect(:request, fn :get, url, headers, _body, _opts -> Registry.register(ClientMock, url, 0) + body = headers |> Enum.into(%{}) |> Jason.encode!() {:ok, 200, [ {"content-type", "application/json"}, - {"content-length", byte_size(json) |> to_string()} - ], %{url: url}} + {"content-length", byte_size(body) |> to_string()} + ], %{url: url, body: body}} end) - |> expect(:stream_body, invokes, fn %{url: url} = client -> + |> expect(:stream_body, invokes, fn %{url: url, body: body} = client -> case Registry.lookup(ClientMock, url) do [{_, 0}] -> Registry.update_value(ClientMock, url, &(&1 + 1)) - {:ok, json, client} + {:ok, body, client} [{_, 1}] -> Registry.unregister(ClientMock, url) @@ -47,7 +45,7 @@ defp user_agent_mock(user_agent, invokes) do describe "reverse proxy" do test "do not track successful request", %{conn: conn} do - user_agent_mock("hackney/1.15.1", 2) + request_mock(2) url = "/success" conn = ReverseProxy.call(conn, url) @@ -57,18 +55,15 @@ test "do not track successful request", %{conn: conn} do end end - describe "user-agent" do - test "don't keep", %{conn: conn} do - user_agent_mock("hackney/1.15.1", 2) - conn = ReverseProxy.call(conn, "/user-agent") - assert json_response(conn, 200) == %{"user-agent" => "hackney/1.15.1"} - end + test "use Pleroma's user agent in the request; don't pass the client's", %{conn: conn} do + request_mock(2) - test "keep", %{conn: conn} do - user_agent_mock(Pleroma.Application.user_agent(), 2) - conn = ReverseProxy.call(conn, "/user-agent-keep", keep_user_agent: true) - assert json_response(conn, 200) == %{"user-agent" => Pleroma.Application.user_agent()} - end + conn = + conn + |> Plug.Conn.put_req_header("user-agent", "fake/1.0") + |> ReverseProxy.call("/user-agent") + + assert json_response(conn, 200) == %{"user-agent" => Pleroma.Application.user_agent()} end test "closed connection", %{conn: conn} do @@ -115,7 +110,7 @@ defp stream_mock(invokes, with_close? \\ false) do describe "max_body" do test "length returns error if content-length more than option", %{conn: conn} do - user_agent_mock("hackney/1.15.1", 0) + request_mock(0) assert capture_log(fn -> ReverseProxy.call(conn, "/huge-file", max_body_length: 4) diff --git a/test/pleroma/runtime_test.exs b/test/pleroma/runtime_test.exs index 010594fcd..b9e769602 100644 --- a/test/pleroma/runtime_test.exs +++ b/test/pleroma/runtime_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.RuntimeTest do diff --git a/test/pleroma/safe_jsonb_set_test.exs b/test/pleroma/safe_jsonb_set_test.exs index 8b1274545..69d696c1b 100644 --- a/test/pleroma/safe_jsonb_set_test.exs +++ b/test/pleroma/safe_jsonb_set_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.SafeJsonbSetTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true test "it doesn't wipe the object when asked to set the value to NULL" do assert %{rows: [[%{"key" => "value", "test" => nil}]]} = diff --git a/test/pleroma/scheduled_activity_test.exs b/test/pleroma/scheduled_activity_test.exs index 7faa5660d..ef91e9bce 100644 --- a/test/pleroma/scheduled_activity_test.exs +++ b/test/pleroma/scheduled_activity_test.exs @@ -1,22 +1,21 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.ScheduledActivityTest do use Pleroma.DataCase - alias Pleroma.DataCase + alias Pleroma.ScheduledActivity + import Pleroma.Factory setup do: clear_config([ScheduledActivity, :enabled]) - setup context do - DataCase.ensure_local_uploader(context) - end + setup [:ensure_local_uploader] describe "creation" do test "scheduled activities with jobs when ScheduledActivity enabled" do - Pleroma.Config.put([ScheduledActivity, :enabled], true) + clear_config([ScheduledActivity, :enabled], true) user = insert(:user) today = @@ -35,7 +34,7 @@ test "scheduled activities with jobs when ScheduledActivity enabled" do end test "scheduled activities without jobs when ScheduledActivity disabled" do - Pleroma.Config.put([ScheduledActivity, :enabled], false) + clear_config([ScheduledActivity, :enabled], false) user = insert(:user) today = diff --git a/test/pleroma/signature_test.exs b/test/pleroma/signature_test.exs index a7a75aa4d..047c537e0 100644 --- a/test/pleroma/signature_test.exs +++ b/test/pleroma/signature_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.SignatureTest do diff --git a/test/pleroma/stats_test.exs b/test/pleroma/stats_test.exs index 74bf785b0..fd3195969 100644 --- a/test/pleroma/stats_test.exs +++ b/test/pleroma/stats_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.StatsTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory diff --git a/test/pleroma/upload/filter/anonymize_filename_test.exs b/test/pleroma/upload/filter/anonymize_filename_test.exs index 7ef01ce91..9387c1abc 100644 --- a/test/pleroma/upload/filter/anonymize_filename_test.exs +++ b/test/pleroma/upload/filter/anonymize_filename_test.exs @@ -1,11 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do use Pleroma.DataCase - alias Pleroma.Config alias Pleroma.Upload setup do @@ -23,13 +22,13 @@ defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do setup do: clear_config([Pleroma.Upload.Filter.AnonymizeFilename, :text]) test "it replaces filename on pre-defined text", %{upload_file: upload_file} do - Config.put([Upload.Filter.AnonymizeFilename, :text], "custom-file.png") + clear_config([Upload.Filter.AnonymizeFilename, :text], "custom-file.png") {:ok, :filtered, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file) assert name == "custom-file.png" end test "it replaces filename on pre-defined text expression", %{upload_file: upload_file} do - Config.put([Upload.Filter.AnonymizeFilename, :text], "custom-file.{extension}") + clear_config([Upload.Filter.AnonymizeFilename, :text], "custom-file.{extension}") {:ok, :filtered, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file) assert name == "custom-file.jpg" end diff --git a/test/pleroma/upload/filter/dedupe_test.exs b/test/pleroma/upload/filter/dedupe_test.exs index 92a3d7df3..f00ba12f9 100644 --- a/test/pleroma/upload/filter/dedupe_test.exs +++ b/test/pleroma/upload/filter/dedupe_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.DedupeTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Upload alias Pleroma.Upload.Filter.Dedupe diff --git a/test/pleroma/upload/filter/exiftool_test.exs b/test/pleroma/upload/filter/exiftool_test.exs index 6b978b64c..cfbe34be8 100644 --- a/test/pleroma/upload/filter/exiftool_test.exs +++ b/test/pleroma/upload/filter/exiftool_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.ExiftoolTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Upload.Filter test "apply exiftool filter" do diff --git a/test/pleroma/upload/filter/mogrifun_test.exs b/test/pleroma/upload/filter/mogrifun_test.exs index fc2f68276..d2b183e90 100644 --- a/test/pleroma/upload/filter/mogrifun_test.exs +++ b/test/pleroma/upload/filter/mogrifun_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.MogrifunTest do diff --git a/test/pleroma/upload/filter/mogrify_test.exs b/test/pleroma/upload/filter/mogrify_test.exs index 6dee02e8b..d62cd83b4 100644 --- a/test/pleroma/upload/filter/mogrify_test.exs +++ b/test/pleroma/upload/filter/mogrify_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.Filter.MogrifyTest do diff --git a/test/pleroma/upload/filter_test.exs b/test/pleroma/upload/filter_test.exs index 09394929c..f0053ed9b 100644 --- a/test/pleroma/upload/filter_test.exs +++ b/test/pleroma/upload/filter_test.exs @@ -1,17 +1,16 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Upload.FilterTest do use Pleroma.DataCase - alias Pleroma.Config alias Pleroma.Upload.Filter setup do: clear_config([Pleroma.Upload.Filter.AnonymizeFilename, :text]) test "applies filters" do - Config.put([Pleroma.Upload.Filter.AnonymizeFilename, :text], "custom-file.png") + clear_config([Pleroma.Upload.Filter.AnonymizeFilename, :text], "custom-file.png") File.cp!( "test/fixtures/image.jpg", diff --git a/test/pleroma/upload_test.exs b/test/pleroma/upload_test.exs index f52d4dff6..f1ab82a57 100644 --- a/test/pleroma/upload_test.exs +++ b/test/pleroma/upload_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.UploadTest do @@ -133,7 +133,7 @@ test "returns a media url" do assert %{"url" => [%{"href" => url}]} = data - assert String.starts_with?(url, Pleroma.Web.base_url() <> "/media/") + assert String.starts_with?(url, Pleroma.Upload.base_url()) end test "copies the file to the configured folder with deduping" do @@ -148,8 +148,8 @@ test "copies the file to the configured folder with deduping" do {:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.Dedupe]) assert List.first(data["url"])["href"] == - Pleroma.Web.base_url() <> - "/media/e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg" + Pleroma.Upload.base_url() <> + "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg" end test "copies the file to the configured folder without deduping" do diff --git a/test/pleroma/uploaders/local_test.exs b/test/pleroma/uploaders/local_test.exs index 1ce7be485..0a5952f50 100644 --- a/test/pleroma/uploaders/local_test.exs +++ b/test/pleroma/uploaders/local_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Uploaders.LocalTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Uploaders.Local describe "get_file/1" do diff --git a/test/pleroma/uploaders/s3_test.exs b/test/pleroma/uploaders/s3_test.exs index e7a013dd8..2711e2c8d 100644 --- a/test/pleroma/uploaders/s3_test.exs +++ b/test/pleroma/uploaders/s3_test.exs @@ -1,21 +1,21 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Uploaders.S3Test do use Pleroma.DataCase - alias Pleroma.Config alias Pleroma.Uploaders.S3 import Mock import ExUnit.CaptureLog - setup do: - clear_config(Pleroma.Uploaders.S3, - bucket: "test_bucket", - public_endpoint: "https://s3.amazonaws.com" - ) + setup do + clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.S3) + clear_config([Pleroma.Upload, :base_url], "https://s3.amazonaws.com") + clear_config([Pleroma.Uploaders.S3]) + clear_config([Pleroma.Uploaders.S3, :bucket], "test_bucket") + end describe "get_file/1" do test "it returns path to local folder for files" do @@ -26,12 +26,14 @@ test "it returns path to local folder for files" do end test "it returns path without bucket when truncated_namespace set to ''" do - Config.put([Pleroma.Uploaders.S3], + clear_config([Pleroma.Uploaders.S3], bucket: "test_bucket", - public_endpoint: "https://s3.amazonaws.com", + bucket_namespace: "myaccount", truncated_namespace: "" ) + clear_config([Pleroma.Upload, :base_url], "https://s3.amazonaws.com") + assert S3.get_file("test_image.jpg") == { :ok, {:url, "https://s3.amazonaws.com/test_image.jpg"} @@ -39,9 +41,8 @@ test "it returns path without bucket when truncated_namespace set to ''" do end test "it returns path with bucket namespace when namespace is set" do - Config.put([Pleroma.Uploaders.S3], + clear_config([Pleroma.Uploaders.S3], bucket: "test_bucket", - public_endpoint: "https://s3.amazonaws.com", bucket_namespace: "family" ) diff --git a/test/pleroma/user/backup_test.exs b/test/pleroma/user/backup_test.exs index f68e4a029..b16152876 100644 --- a/test/pleroma/user/backup_test.exs +++ b/test/pleroma/user/backup_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.BackupTest do @@ -23,7 +23,7 @@ defmodule Pleroma.User.BackupTest do end test "it requries enabled email" do - Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false) + clear_config([Pleroma.Emails.Mailer, :enabled], false) user = insert(:user) assert {:error, "Backups require enabled email"} == Backup.create(user) end @@ -53,7 +53,7 @@ test "it return an error if the export limit is over" do end test "it process a backup record" do - Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) %{id: user_id} = user = insert(:user) assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id} = args}} = Backup.create(user) @@ -76,8 +76,8 @@ test "it process a backup record" do end test "it removes outdated backups after creating a fresh one" do - Pleroma.Config.put([Backup, :limit_days], -1) - Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + clear_config([Backup, :limit_days], -1) + clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) user = insert(:user) assert {:ok, job1} = Backup.create(user) @@ -195,12 +195,8 @@ test "it creates a zip archive with user data" do describe "it uploads and deletes a backup archive" do setup do - clear_config(Pleroma.Uploaders.S3, - bucket: "test_bucket", - public_endpoint: "https://s3.amazonaws.com" - ) - - clear_config([Pleroma.Upload, :uploader]) + clear_config([Pleroma.Upload, :base_url], "https://s3.amazonaws.com") + clear_config([Pleroma.Uploaders.S3, :bucket], "test_bucket") user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"}) @@ -219,7 +215,8 @@ test "it creates a zip archive with user data" do end test "S3", %{path: path, backup: backup} do - Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.S3) + clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.S3) + clear_config([Pleroma.Uploaders.S3, :streaming_enabled], false) with_mock ExAws, request: fn @@ -229,13 +226,10 @@ test "S3", %{path: path, backup: backup} do assert {:ok, %Pleroma.Upload{}} = Backup.upload(backup, path) assert {:ok, _backup} = Backup.delete(backup) end - - with_mock ExAws, request: fn %{http_method: :delete} -> {:ok, %{status_code: 204}} end do - end end test "Local", %{path: path, backup: backup} do - Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) + clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) assert {:ok, %Pleroma.Upload{}} = Backup.upload(backup, path) assert {:ok, _backup} = Backup.delete(backup) diff --git a/test/pleroma/user/import_test.exs b/test/pleroma/user/import_test.exs index e198cdc08..a84fce337 100644 --- a/test/pleroma/user/import_test.exs +++ b/test/pleroma/user/import_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.ImportTest do diff --git a/test/pleroma/user/notification_setting_test.exs b/test/pleroma/user/notification_setting_test.exs index 308da216a..6cb8803d9 100644 --- a/test/pleroma/user/notification_setting_test.exs +++ b/test/pleroma/user/notification_setting_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.NotificationSettingTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.User.NotificationSetting diff --git a/test/pleroma/user/query_test.exs b/test/pleroma/user/query_test.exs index e2f5c7d81..357016e3e 100644 --- a/test/pleroma/user/query_test.exs +++ b/test/pleroma/user/query_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.QueryTest do diff --git a/test/pleroma/user/welcome_chat_message_test.exs b/test/pleroma/user/welcome_chat_message_test.exs index fe26d6e4d..42a45fa19 100644 --- a/test/pleroma/user/welcome_chat_message_test.exs +++ b/test/pleroma/user/welcome_chat_message_test.exs @@ -1,11 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.WelcomeChatMessageTest do use Pleroma.DataCase - alias Pleroma.Config alias Pleroma.User.WelcomeChatMessage import Pleroma.Factory @@ -17,10 +16,10 @@ test "send a chat welcome message" do welcome_user = insert(:user, name: "mewmew") user = insert(:user) - Config.put([:welcome, :chat_message, :enabled], true) - Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) + clear_config([:welcome, :chat_message, :enabled], true) + clear_config([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) - Config.put( + clear_config( [:welcome, :chat_message, :message], "Hello, welcome to Blob/Cat!" ) @@ -28,8 +27,10 @@ test "send a chat welcome message" do {:ok, %Pleroma.Activity{} = activity} = WelcomeChatMessage.post_message(user) assert user.ap_id in activity.recipients - assert Pleroma.Object.normalize(activity).data["type"] == "ChatMessage" - assert Pleroma.Object.normalize(activity).data["content"] == "Hello, welcome to Blob/Cat!" + assert Pleroma.Object.normalize(activity, fetch: false).data["type"] == "ChatMessage" + + assert Pleroma.Object.normalize(activity, fetch: false).data["content"] == + "Hello, welcome to Blob/Cat!" end end end diff --git a/test/pleroma/user/welcome_email_test.exs b/test/pleroma/user/welcome_email_test.exs index d005d11b2..c3d383a7f 100644 --- a/test/pleroma/user/welcome_email_test.exs +++ b/test/pleroma/user/welcome_email_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.WelcomeEmailTest do @@ -18,15 +18,15 @@ defmodule Pleroma.User.WelcomeEmailTest do test "send a welcome email" do user = insert(:user, name: "Jimm") - Config.put([:welcome, :email, :enabled], true) - Config.put([:welcome, :email, :sender], "welcome@pleroma.app") + clear_config([:welcome, :email, :enabled], true) + clear_config([:welcome, :email, :sender], "welcome@pleroma.app") - Config.put( + clear_config( [:welcome, :email, :subject], "Hello, welcome to pleroma: <%= instance_name %>" ) - Config.put( + clear_config( [:welcome, :email, :html], "

Hello <%= user.name %>.

Welcome to <%= instance_name %>

" ) @@ -44,7 +44,7 @@ test "send a welcome email" do html_body: "

Hello #{user.name}.

Welcome to #{instance_name}

" ) - Config.put([:welcome, :email, :sender], {"Pleroma App", "welcome@pleroma.app"}) + clear_config([:welcome, :email, :sender], {"Pleroma App", "welcome@pleroma.app"}) {:ok, _job} = WelcomeEmail.send_email(user) diff --git a/test/pleroma/user/welcome_message_test.exs b/test/pleroma/user/welcome_message_test.exs index 3cd6f5cb7..28afde943 100644 --- a/test/pleroma/user/welcome_message_test.exs +++ b/test/pleroma/user/welcome_message_test.exs @@ -1,11 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.WelcomeMessageTest do use Pleroma.DataCase - alias Pleroma.Config alias Pleroma.User.WelcomeMessage import Pleroma.Factory @@ -17,10 +16,10 @@ test "send a direct welcome message" do welcome_user = insert(:user) user = insert(:user, name: "Jimm") - Config.put([:welcome, :direct_message, :enabled], true) - Config.put([:welcome, :direct_message, :sender_nickname], welcome_user.nickname) + clear_config([:welcome, :direct_message, :enabled], true) + clear_config([:welcome, :direct_message, :sender_nickname], welcome_user.nickname) - Config.put( + clear_config( [:welcome, :direct_message, :message], "Hello. Welcome to Pleroma" ) @@ -28,7 +27,9 @@ test "send a direct welcome message" do {:ok, %Pleroma.Activity{} = activity} = WelcomeMessage.post_message(user) assert user.ap_id in activity.recipients assert activity.data["directMessage"] == true - assert Pleroma.Object.normalize(activity).data["content"] =~ "Hello. Welcome to Pleroma" + + assert Pleroma.Object.normalize(activity, fetch: false).data["content"] =~ + "Hello. Welcome to Pleroma" end end end diff --git a/test/pleroma/user_invite_token_test.exs b/test/pleroma/user_invite_token_test.exs index 63f18f13c..233d4e864 100644 --- a/test/pleroma/user_invite_token_test.exs +++ b/test/pleroma/user_invite_token_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.UserInviteTokenTest do diff --git a/test/pleroma/user_relationship_test.exs b/test/pleroma/user_relationship_test.exs index f12406097..b2b074607 100644 --- a/test/pleroma/user_relationship_test.exs +++ b/test/pleroma/user_relationship_test.exs @@ -1,11 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.UserRelationshipTest do alias Pleroma.UserRelationship - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory diff --git a/test/pleroma/user_search_test.exs b/test/pleroma/user_search_test.exs index accb0b816..69167bb0c 100644 --- a/test/pleroma/user_search_test.exs +++ b/test/pleroma/user_search_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.UserSearchTest do @@ -18,7 +18,7 @@ defmodule Pleroma.UserSearchTest do setup do: clear_config([:instance, :limit_to_local_content]) test "returns a resolved user as the first result" do - Pleroma.Config.put([:instance, :limit_to_local_content], false) + clear_config([:instance, :limit_to_local_content], false) user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"}) _user = insert(:user, %{nickname: "com_user"}) @@ -199,7 +199,7 @@ test "find only local users for unauthenticated users" do end test "find only local users for authenticated users when `limit_to_local_content` is `:all`" do - Pleroma.Config.put([:instance, :limit_to_local_content], :all) + clear_config([:instance, :limit_to_local_content], :all) %{id: id} = insert(:user, %{name: "lain"}) insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false}) @@ -209,7 +209,7 @@ test "find only local users for authenticated users when `limit_to_local_content end test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do - Pleroma.Config.put([:instance, :limit_to_local_content], false) + clear_config([:instance, :limit_to_local_content], false) u1 = insert(:user, %{name: "lain"}) u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false}) diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index 5d544fe7d..d81c1b8eb 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.UserTest do @@ -202,11 +202,11 @@ test "doesn't return already accepted or duplicate follow requests" do test "doesn't return follow requests for deactivated accounts" do locked = insert(:user, is_locked: true) - pending_follower = insert(:user, %{deactivated: true}) + pending_follower = insert(:user, %{is_active: false}) CommonAPI.follow(pending_follower, locked) - assert true == pending_follower.deactivated + refute pending_follower.is_active assert [] = User.get_follow_requests(locked) end @@ -275,7 +275,7 @@ test "follow takes a user and another user" do test "can't follow a deactivated users" do user = insert(:user) - followed = insert(:user, %{deactivated: true}) + followed = insert(:user, %{is_active: false}) {:error, _} = User.follow(user, followed) end @@ -311,7 +311,7 @@ test "local users do not automatically follow local locked accounts" do setup do: clear_config([:instance, :external_user_synchronization]) test "unfollow with syncronizes external user" do - Pleroma.Config.put([:instance, :external_user_synchronization], true) + clear_config([:instance, :external_user_synchronization], true) followed = insert(:user, @@ -396,7 +396,7 @@ test "it autofollows accounts that are set for it" do user = insert(:user) remote_user = insert(:user, %{local: false}) - Pleroma.Config.put([:instance, :autofollowed_nicknames], [ + clear_config([:instance, :autofollowed_nicknames], [ user.nickname, remote_user.nickname ]) @@ -413,7 +413,7 @@ test "it adds automatic followers for new registered accounts" do user1 = insert(:user) user2 = insert(:user) - Pleroma.Config.put([:instance, :autofollowing_nicknames], [ + clear_config([:instance, :autofollowing_nicknames], [ user1.nickname, user2.nickname ]) @@ -428,9 +428,9 @@ test "it adds automatic followers for new registered accounts" do test "it sends a welcome message if it is set" do welcome_user = insert(:user) - Pleroma.Config.put([:welcome, :direct_message, :enabled], true) - Pleroma.Config.put([:welcome, :direct_message, :sender_nickname], welcome_user.nickname) - Pleroma.Config.put([:welcome, :direct_message, :message], "Hello, this is a direct message") + clear_config([:welcome, :direct_message, :enabled], true) + clear_config([:welcome, :direct_message, :sender_nickname], welcome_user.nickname) + clear_config([:welcome, :direct_message, :message], "Hello, this is a direct message") cng = User.register_changeset(%User{}, @full_user_data) {:ok, registered_user} = User.register(cng) @@ -438,15 +438,15 @@ test "it sends a welcome message if it is set" do activity = Repo.one(Pleroma.Activity) assert registered_user.ap_id in activity.recipients - assert Object.normalize(activity).data["content"] =~ "direct message" + assert Object.normalize(activity, fetch: false).data["content"] =~ "direct message" assert activity.actor == welcome_user.ap_id end test "it sends a welcome chat message if it is set" do welcome_user = insert(:user) - Pleroma.Config.put([:welcome, :chat_message, :enabled], true) - Pleroma.Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) - Pleroma.Config.put([:welcome, :chat_message, :message], "Hello, this is a chat message") + clear_config([:welcome, :chat_message, :enabled], true) + clear_config([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) + clear_config([:welcome, :chat_message, :message], "Hello, this is a chat message") cng = User.register_changeset(%User{}, @full_user_data) {:ok, registered_user} = User.register(cng) @@ -454,7 +454,7 @@ test "it sends a welcome chat message if it is set" do activity = Repo.one(Pleroma.Activity) assert registered_user.ap_id in activity.recipients - assert Object.normalize(activity).data["content"] =~ "chat message" + assert Object.normalize(activity, fetch: false).data["content"] =~ "chat message" assert activity.actor == welcome_user.ap_id end @@ -480,12 +480,12 @@ test "it sends a welcome chat message if it is set" do ) test "it sends a welcome chat message when Simple policy applied to local instance" do - Pleroma.Config.put([:mrf_simple, :media_nsfw], ["localhost"]) + clear_config([:mrf_simple, :media_nsfw], ["localhost"]) welcome_user = insert(:user) - Pleroma.Config.put([:welcome, :chat_message, :enabled], true) - Pleroma.Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) - Pleroma.Config.put([:welcome, :chat_message, :message], "Hello, this is a chat message") + clear_config([:welcome, :chat_message, :enabled], true) + clear_config([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) + clear_config([:welcome, :chat_message, :message], "Hello, this is a chat message") cng = User.register_changeset(%User{}, @full_user_data) {:ok, registered_user} = User.register(cng) @@ -493,16 +493,16 @@ test "it sends a welcome chat message when Simple policy applied to local instan activity = Repo.one(Pleroma.Activity) assert registered_user.ap_id in activity.recipients - assert Object.normalize(activity).data["content"] =~ "chat message" + assert Object.normalize(activity, fetch: false).data["content"] =~ "chat message" assert activity.actor == welcome_user.ap_id end test "it sends a welcome email message if it is set" do welcome_user = insert(:user) - Pleroma.Config.put([:welcome, :email, :enabled], true) - Pleroma.Config.put([:welcome, :email, :sender], welcome_user.email) + clear_config([:welcome, :email, :enabled], true) + clear_config([:welcome, :email, :sender], welcome_user.email) - Pleroma.Config.put( + clear_config( [:welcome, :email, :subject], "Hello, welcome to cool site: <%= instance_name %>" ) @@ -522,7 +522,7 @@ test "it sends a welcome email message if it is set" do end test "it sends a confirm email" do - Pleroma.Config.put([:instance, :account_activation_required], true) + clear_config([:instance, :account_activation_required], true) cng = User.register_changeset(%User{}, @full_user_data) {:ok, registered_user} = User.register(cng) @@ -535,8 +535,45 @@ test "it sends a confirm email" do |> assert_email_sent() end + test "sends a pending approval email" do + clear_config([:instance, :account_approval_required], true) + + {:ok, user} = + User.register_changeset(%User{}, @full_user_data) + |> User.register() + + ObanHelpers.perform_all() + + assert_email_sent( + from: Pleroma.Config.Helpers.sender(), + to: {user.name, user.email}, + subject: "Your account is awaiting approval" + ) + end + + test "it sends a registration confirmed email if no others will be sent" do + clear_config([:welcome, :email, :enabled], false) + clear_config([:instance, :account_activation_required], false) + clear_config([:instance, :account_approval_required], false) + + {:ok, user} = + User.register_changeset(%User{}, @full_user_data) + |> User.register() + + ObanHelpers.perform_all() + + instance_name = Pleroma.Config.get([:instance, :name]) + sender = Pleroma.Config.get([:instance, :notify_email]) + + assert_email_sent( + from: {instance_name, sender}, + to: {user.name, user.email}, + subject: "Account registered on #{instance_name}" + ) + end + test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do - Pleroma.Config.put([:instance, :account_activation_required], true) + clear_config([:instance, :account_activation_required], true) @full_user_data |> Map.keys() @@ -549,7 +586,7 @@ test "it requires an email, name, nickname and password, bio is optional when ac end test "it requires an name, nickname and password, bio and email are optional when account_activation_required is disabled" do - Pleroma.Config.put([:instance, :account_activation_required], false) + clear_config([:instance, :account_activation_required], false) @full_user_data |> Map.keys() @@ -624,7 +661,7 @@ test "it creates a confirmed user" do {:ok, user} = Repo.insert(changeset) - refute user.confirmation_pending + assert user.is_confirmed end end @@ -645,17 +682,17 @@ test "it creates unconfirmed user" do {:ok, user} = Repo.insert(changeset) - assert user.confirmation_pending + refute user.is_confirmed assert user.confirmation_token end test "it creates confirmed user if :confirmed option is given" do - changeset = User.register_changeset(%User{}, @full_user_data, need_confirmation: false) + changeset = User.register_changeset(%User{}, @full_user_data, confirmed: true) assert changeset.valid? {:ok, user} = Repo.insert(changeset) - refute user.confirmation_pending + assert user.is_confirmed refute user.confirmation_token end end @@ -678,7 +715,7 @@ test "it creates unapproved user" do {:ok, user} = Repo.insert(changeset) - assert user.approval_pending + refute user.is_approved assert user.registration_reason == "I'm a cool guy :)" end @@ -1297,14 +1334,14 @@ test "has following" do end end - describe ".deactivate" do + describe ".set_activation" do test "can de-activate then re-activate a user" do user = insert(:user) - assert false == user.deactivated - {:ok, user} = User.deactivate(user) - assert true == user.deactivated - {:ok, user} = User.deactivate(user, false) - assert false == user.deactivated + assert user.is_active + {:ok, user} = User.set_activation(user, false) + refute user.is_active + {:ok, user} = User.set_activation(user, true) + assert user.is_active end test "hide a user from followers" do @@ -1312,7 +1349,7 @@ test "hide a user from followers" do user2 = insert(:user) {:ok, user, user2} = User.follow(user, user2) - {:ok, _user} = User.deactivate(user) + {:ok, _user} = User.set_activation(user, false) user2 = User.get_cached_by_id(user2.id) @@ -1328,7 +1365,7 @@ test "hide a user from friends" do assert user2.following_count == 1 assert User.following_count(user2) == 1 - {:ok, _user} = User.deactivate(user) + {:ok, _user} = User.set_activation(user, false) user2 = User.get_cached_by_id(user2.id) @@ -1358,7 +1395,7 @@ test "hide a user's statuses from timelines and notifications" do user: user2 }) - {:ok, _user} = User.deactivate(user) + {:ok, _user} = User.set_activation(user, false) assert [] == ActivityPub.fetch_public_activities(%{}) assert [] == Pleroma.Notification.for_user(user2) @@ -1372,17 +1409,17 @@ test "hide a user's statuses from timelines and notifications" do describe "approve" do test "approves a user" do - user = insert(:user, approval_pending: true) - assert true == user.approval_pending + user = insert(:user, is_approved: false) + refute user.is_approved {:ok, user} = User.approve(user) - assert false == user.approval_pending + assert user.is_approved end test "approves a list of users" do unapproved_users = [ - insert(:user, approval_pending: true), - insert(:user, approval_pending: true), - insert(:user, approval_pending: true) + insert(:user, is_approved: false), + insert(:user, is_approved: false), + insert(:user, is_approved: false) ] {:ok, users} = User.approve(unapproved_users) @@ -1390,9 +1427,101 @@ test "approves a list of users" do assert Enum.count(users) == 3 Enum.each(users, fn user -> - assert false == user.approval_pending + assert user.is_approved end) end + + test "it sends welcome email if it is set" do + clear_config([:welcome, :email, :enabled], true) + clear_config([:welcome, :email, :sender], "tester@test.me") + + user = insert(:user, is_approved: false) + welcome_user = insert(:user, email: "tester@test.me") + instance_name = Pleroma.Config.get([:instance, :name]) + + User.approve(user) + + ObanHelpers.perform_all() + + assert_email_sent( + from: {instance_name, welcome_user.email}, + to: {user.name, user.email}, + html_body: "Welcome to #{instance_name}" + ) + end + + test "approving an approved user does not trigger post-register actions" do + clear_config([:welcome, :email, :enabled], true) + + user = insert(:user, is_approved: true) + User.approve(user) + + ObanHelpers.perform_all() + + assert_no_email_sent() + end + end + + describe "confirm" do + test "confirms a user" do + user = insert(:user, is_confirmed: false) + refute user.is_confirmed + {:ok, user} = User.confirm(user) + assert user.is_confirmed + end + + test "confirms a list of users" do + unconfirmed_users = [ + insert(:user, is_confirmed: false), + insert(:user, is_confirmed: false), + insert(:user, is_confirmed: false) + ] + + {:ok, users} = User.confirm(unconfirmed_users) + + assert Enum.count(users) == 3 + + Enum.each(users, fn user -> + assert user.is_confirmed + end) + end + + test "sends approval emails when `is_approved: false`" do + admin = insert(:user, is_admin: true) + user = insert(:user, is_confirmed: false, is_approved: false) + User.confirm(user) + + ObanHelpers.perform_all() + + user_email = Pleroma.Emails.UserEmail.approval_pending_email(user) + admin_email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user) + + notify_email = Pleroma.Config.get([:instance, :notify_email]) + instance_name = Pleroma.Config.get([:instance, :name]) + + # User approval email + assert_email_sent( + from: {instance_name, notify_email}, + to: {user.name, user.email}, + html_body: user_email.html_body + ) + + # Admin email + assert_email_sent( + from: {instance_name, notify_email}, + to: {admin.name, admin.email}, + html_body: admin_email.html_body + ) + end + + test "confirming a confirmed user does not trigger post-register actions" do + user = insert(:user, is_confirmed: true, is_approved: false) + User.confirm(user) + + ObanHelpers.perform_all() + + assert_no_email_sent() + end end describe "delete" do @@ -1436,7 +1565,7 @@ test "it deactivates a user, all follow relationships and all activities", %{use follower = User.get_cached_by_id(follower.id) refute User.following?(follower, user) - assert %{deactivated: true} = User.get_by_id(user.id) + assert %{is_active: false} = User.get_by_id(user.id) assert [] == User.get_follow_requests(locked_user) @@ -1455,35 +1584,19 @@ test "it deactivates a user, all follow relationships and all activities", %{use end end - describe "delete/1 when confirmation is pending" do - setup do - user = insert(:user, confirmation_pending: true) - {:ok, user: user} - end + test "delete/1 when confirmation is pending deletes the user" do + clear_config([:instance, :account_activation_required], true) + user = insert(:user, is_confirmed: false) - test "deletes user from database when activation required", %{user: user} do - clear_config([:instance, :account_activation_required], true) + {:ok, job} = User.delete(user) + {:ok, _} = ObanHelpers.perform(job) - {:ok, job} = User.delete(user) - {:ok, _} = ObanHelpers.perform(job) - - refute User.get_cached_by_id(user.id) - refute User.get_by_id(user.id) - end - - test "deactivates user when activation is not required", %{user: user} do - clear_config([:instance, :account_activation_required], false) - - {:ok, job} = User.delete(user) - {:ok, _} = ObanHelpers.perform(job) - - assert %{deactivated: true} = User.get_cached_by_id(user.id) - assert %{deactivated: true} = User.get_by_id(user.id) - end + refute User.get_cached_by_id(user.id) + refute User.get_by_id(user.id) end test "delete/1 when approval is pending deletes the user" do - user = insert(:user, approval_pending: true) + user = insert(:user, is_approved: false) {:ok, job} = User.delete(user) {:ok, _} = ObanHelpers.perform(job) @@ -1508,13 +1621,13 @@ test "delete/1 purges a user when they wouldn't be fully deleted" do follower_count: 9, following_count: 9001, is_locked: true, - confirmation_pending: true, + is_confirmed: false, password_reset_pending: true, - approval_pending: true, + is_approved: false, registration_reason: "ahhhhh", confirmation_token: "qqqq", domain_blocks: ["lain.com"], - deactivated: true, + is_active: false, ap_enabled: true, is_moderator: true, is_admin: true, @@ -1550,13 +1663,13 @@ test "delete/1 purges a user when they wouldn't be fully deleted" do follower_count: 0, following_count: 0, is_locked: false, - confirmation_pending: false, + is_confirmed: true, password_reset_pending: false, - approval_pending: false, + is_approved: true, registration_reason: nil, confirmation_token: nil, domain_blocks: [], - deactivated: true, + is_active: false, ap_enabled: false, is_moderator: false, is_admin: false, @@ -1620,14 +1733,14 @@ test "User.delete() plugs any possible zombie objects" do setup do: clear_config([:instance, :account_activation_required]) test "return confirmation_pending for unconfirm user" do - Pleroma.Config.put([:instance, :account_activation_required], true) - user = insert(:user, confirmation_pending: true) + clear_config([:instance, :account_activation_required], true) + user = insert(:user, is_confirmed: false) assert User.account_status(user) == :confirmation_pending end test "return active for confirmed user" do - Pleroma.Config.put([:instance, :account_activation_required], true) - user = insert(:user, confirmation_pending: false) + clear_config([:instance, :account_activation_required], true) + user = insert(:user, is_confirmed: true) assert User.account_status(user) == :active end @@ -1642,15 +1755,15 @@ test "returns :password_reset_pending for user with reset password" do end test "returns :deactivated for deactivated user" do - user = insert(:user, local: true, confirmation_pending: false, deactivated: true) + user = insert(:user, local: true, is_confirmed: true, is_active: false) assert User.account_status(user) == :deactivated end test "returns :approval_pending for unapproved user" do - user = insert(:user, local: true, approval_pending: true) + user = insert(:user, local: true, is_approved: false) assert User.account_status(user) == :approval_pending - user = insert(:user, local: true, confirmation_pending: true, approval_pending: true) + user = insert(:user, local: true, is_confirmed: false, is_approved: false) assert User.account_status(user) == :approval_pending end end @@ -1705,34 +1818,27 @@ test "returns true when the account is itself" do end test "returns false when the account is unconfirmed and confirmation is required" do - Pleroma.Config.put([:instance, :account_activation_required], true) + clear_config([:instance, :account_activation_required], true) - user = insert(:user, local: true, confirmation_pending: true) + user = insert(:user, local: true, is_confirmed: false) other_user = insert(:user, local: true) refute User.visible_for(user, other_user) == :visible end test "returns true when the account is unconfirmed and confirmation is required but the account is remote" do - Pleroma.Config.put([:instance, :account_activation_required], true) + clear_config([:instance, :account_activation_required], true) - user = insert(:user, local: false, confirmation_pending: true) - other_user = insert(:user, local: true) - - assert User.visible_for(user, other_user) == :visible - end - - test "returns true when the account is unconfirmed and confirmation is not required" do - user = insert(:user, local: true, confirmation_pending: true) + user = insert(:user, local: false, is_confirmed: false) other_user = insert(:user, local: true) assert User.visible_for(user, other_user) == :visible end test "returns true when the account is unconfirmed and being viewed by a privileged account (confirmation required)" do - Pleroma.Config.put([:instance, :account_activation_required], true) + clear_config([:instance, :account_activation_required], true) - user = insert(:user, local: true, confirmation_pending: true) + user = insert(:user, local: true, is_confirmed: false) other_user = insert(:user, local: true, is_admin: true) assert User.visible_for(user, other_user) == :visible @@ -1800,7 +1906,7 @@ test "Users are inactive by default" do users = Enum.map(1..total, fn _ -> - insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false) + insert(:user, last_digest_emailed_at: days_ago(20), is_active: true) end) inactive_users_ids = @@ -1818,7 +1924,7 @@ test "Only includes users who has no recent activity" do users = Enum.map(1..total, fn _ -> - insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false) + insert(:user, last_digest_emailed_at: days_ago(20), is_active: true) end) {inactive, active} = Enum.split(users, trunc(total / 2)) @@ -1851,7 +1957,7 @@ test "Only includes users with no read notifications" do users = Enum.map(1..total, fn _ -> - insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false) + insert(:user, last_digest_emailed_at: days_ago(20), is_active: true) end) [sender | recipients] = users @@ -1889,24 +1995,6 @@ test "Only includes users with no read notifications" do end end - describe "toggle_confirmation/1" do - test "if user is confirmed" do - user = insert(:user, confirmation_pending: false) - {:ok, user} = User.toggle_confirmation(user) - - assert user.confirmation_pending - assert user.confirmation_token - end - - test "if user is unconfirmed" do - user = insert(:user, confirmation_pending: true, confirmation_token: "some token") - {:ok, user} = User.toggle_confirmation(user) - - refute user.confirmation_pending - refute user.confirmation_token - end - end - describe "ensure_keys_present" do test "it creates keys for a user and stores them in info" do user = insert(:user) @@ -1939,7 +2027,7 @@ test "it returns a list of AP ids for a given set of nicknames" do user1 = insert(:user, local: false, ap_id: "http://localhost:4001/users/masto_closed") user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2") insert(:user, local: true) - insert(:user, local: false, deactivated: true) + insert(:user, local: false, is_active: false) {:ok, user1: user1, user2: user2} end @@ -2005,7 +2093,7 @@ test "performs update cache if user updated" do setup do: clear_config([:instance, :external_user_synchronization]) test "updates the counters normally on following/getting a follow when disabled" do - Pleroma.Config.put([:instance, :external_user_synchronization], false) + clear_config([:instance, :external_user_synchronization], false) user = insert(:user) other_user = @@ -2026,7 +2114,7 @@ test "updates the counters normally on following/getting a follow when disabled" end test "syncronizes the counters with the remote instance for the followed when enabled" do - Pleroma.Config.put([:instance, :external_user_synchronization], false) + clear_config([:instance, :external_user_synchronization], false) user = insert(:user) @@ -2041,14 +2129,14 @@ test "syncronizes the counters with the remote instance for the followed when en assert other_user.following_count == 0 assert other_user.follower_count == 0 - Pleroma.Config.put([:instance, :external_user_synchronization], true) + clear_config([:instance, :external_user_synchronization], true) {:ok, _user, other_user} = User.follow(user, other_user) assert other_user.follower_count == 437 end test "syncronizes the counters with the remote instance for the follower when enabled" do - Pleroma.Config.put([:instance, :external_user_synchronization], false) + clear_config([:instance, :external_user_synchronization], false) user = insert(:user) @@ -2063,7 +2151,7 @@ test "syncronizes the counters with the remote instance for the follower when en assert other_user.following_count == 0 assert other_user.follower_count == 0 - Pleroma.Config.put([:instance, :external_user_synchronization], true) + clear_config([:instance, :external_user_synchronization], true) {:ok, other_user, _user} = User.follow(other_user, user) assert other_user.following_count == 152 @@ -2110,43 +2198,43 @@ test "changes email", %{user: user} do test "allows getting remote users by id no matter what :limit_to_local_content is set to", %{ remote_user: remote_user } do - Pleroma.Config.put([:instance, :limit_to_local_content], false) + clear_config([:instance, :limit_to_local_content], false) assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id) - Pleroma.Config.put([:instance, :limit_to_local_content], true) + clear_config([:instance, :limit_to_local_content], true) assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id) - Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) + clear_config([:instance, :limit_to_local_content], :unauthenticated) assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id) end test "disallows getting remote users by nickname without authentication when :limit_to_local_content is set to :unauthenticated", %{remote_user: remote_user} do - Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) + clear_config([:instance, :limit_to_local_content], :unauthenticated) assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname) end test "allows getting remote users by nickname with authentication when :limit_to_local_content is set to :unauthenticated", %{remote_user: remote_user, local_user: local_user} do - Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) + clear_config([:instance, :limit_to_local_content], :unauthenticated) assert %User{} = User.get_cached_by_nickname_or_id(remote_user.nickname, for: local_user) end test "disallows getting remote users by nickname when :limit_to_local_content is set to true", %{remote_user: remote_user} do - Pleroma.Config.put([:instance, :limit_to_local_content], true) + clear_config([:instance, :limit_to_local_content], true) assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname) end test "allows getting local users by nickname no matter what :limit_to_local_content is set to", %{local_user: local_user} do - Pleroma.Config.put([:instance, :limit_to_local_content], false) + clear_config([:instance, :limit_to_local_content], false) assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname) - Pleroma.Config.put([:instance, :limit_to_local_content], true) + clear_config([:instance, :limit_to_local_content], true) assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname) - Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) + clear_config([:instance, :limit_to_local_content], :unauthenticated) assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname) end end @@ -2165,6 +2253,36 @@ test "Notifications are updated", %{user: user} do end end + describe "local_nickname/1" do + test "returns nickname without host" do + assert User.local_nickname("@mentioned") == "mentioned" + assert User.local_nickname("a_local_nickname") == "a_local_nickname" + assert User.local_nickname("nickname@host.com") == "nickname" + end + end + + describe "full_nickname/1" do + test "returns fully qualified nickname for local and remote users" do + local_user = + insert(:user, nickname: "local_user", ap_id: "https://somehost.com/users/local_user") + + remote_user = insert(:user, nickname: "remote@host.com", local: false) + + assert User.full_nickname(local_user) == "local_user@somehost.com" + assert User.full_nickname(remote_user) == "remote@host.com" + end + + test "strips leading @ from mentions" do + assert User.full_nickname("@mentioned") == "mentioned" + assert User.full_nickname("@nickname@host.com") == "nickname@host.com" + end + + test "does not modify nicknames" do + assert User.full_nickname("nickname") == "nickname" + assert User.full_nickname("nickname@host.com") == "nickname@host.com" + end + end + test "avatar fallback" do user = insert(:user) assert User.avatar_url(user) =~ "/images/avi.png" @@ -2181,4 +2299,88 @@ test "get_host/1" do user = insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain") assert User.get_host(user) == "lain.com" end + + test "update_last_active_at/1" do + user = insert(:user) + assert is_nil(user.last_active_at) + + test_started_at = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + + assert {:ok, user} = User.update_last_active_at(user) + + assert user.last_active_at >= test_started_at + assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second) + + last_active_at = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(-:timer.hours(24), :millisecond) + |> NaiveDateTime.truncate(:second) + + assert {:ok, user} = + user + |> cast(%{last_active_at: last_active_at}, [:last_active_at]) + |> User.update_and_set_cache() + + assert user.last_active_at == last_active_at + assert {:ok, user} = User.update_last_active_at(user) + assert user.last_active_at >= test_started_at + assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second) + end + + test "active_user_count/1" do + insert(:user) + insert(:user, %{local: false}) + insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -5)}) + insert(:user, %{last_active_at: Timex.shift(NaiveDateTime.utc_now(), weeks: -3)}) + insert(:user, %{last_active_at: NaiveDateTime.utc_now()}) + + assert User.active_user_count() == 2 + assert User.active_user_count(6) == 3 + assert User.active_user_count(1) == 1 + end + + describe "pins" do + setup do + user = insert(:user) + + [user: user, object_id: object_id_from_created_activity(user)] + end + + test "unique pins", %{user: user, object_id: object_id} do + assert {:ok, %{pinned_objects: %{^object_id => pinned_at1} = pins} = updated_user} = + User.add_pinned_object_id(user, object_id) + + assert Enum.count(pins) == 1 + + assert {:ok, %{pinned_objects: %{^object_id => pinned_at2} = pins}} = + User.add_pinned_object_id(updated_user, object_id) + + assert pinned_at1 == pinned_at2 + + assert Enum.count(pins) == 1 + end + + test "respects max_pinned_statuses limit", %{user: user, object_id: object_id} do + clear_config([:instance, :max_pinned_statuses], 1) + {:ok, updated} = User.add_pinned_object_id(user, object_id) + + object_id2 = object_id_from_created_activity(user) + + {:error, %{errors: errors}} = User.add_pinned_object_id(updated, object_id2) + assert Keyword.has_key?(errors, :pinned_objects) + end + + test "remove_pinned_object_id/2", %{user: user, object_id: object_id} do + assert {:ok, updated} = User.add_pinned_object_id(user, object_id) + + {:ok, after_remove} = User.remove_pinned_object_id(updated, object_id) + assert after_remove.pinned_objects == %{} + end + end + + defp object_id_from_created_activity(user) do + %{id: id} = insert(:note_activity, user: user) + %{object: %{data: %{"id" => object_id}}} = Activity.get_by_id_with_object(id) + object_id + end end diff --git a/test/pleroma/utils_test.exs b/test/pleroma/utils_test.exs index 460f7e0b5..c593a9490 100644 --- a/test/pleroma/utils_test.exs +++ b/test/pleroma/utils_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.UtilsTest do diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index 0063d0482..cea4b3a97 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do @@ -7,7 +7,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do use Oban.Testing, repo: Pleroma.Repo alias Pleroma.Activity - alias Pleroma.Config alias Pleroma.Delivery alias Pleroma.Instances alias Pleroma.Object @@ -46,7 +45,7 @@ test "with the relay active, it returns the relay user", %{conn: conn} do end test "with the relay disabled, it returns 404", %{conn: conn} do - Config.put([:instance, :allow_relay], false) + clear_config([:instance, :allow_relay], false) conn |> get(activity_pub_path(conn, :relay)) @@ -54,7 +53,7 @@ test "with the relay disabled, it returns 404", %{conn: conn} do end test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) + clear_config([:instance, :federating], false) user = insert(:user) conn @@ -75,7 +74,7 @@ test "it returns the internal fetch user", %{conn: conn} do end test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) + clear_config([:instance, :federating], false) user = insert(:user) conn @@ -219,7 +218,7 @@ test "it doesn't return a local-only object", %{conn: conn} do assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post) - object = Object.normalize(post, false) + object = Object.normalize(post, fetch: false) uuid = String.split(object.data["id"], "/") |> List.last() conn = @@ -230,6 +229,24 @@ test "it doesn't return a local-only object", %{conn: conn} do assert json_response(conn, 404) end + test "returns local-only objects when authenticated", %{conn: conn} do + user = insert(:user) + {:ok, post} = CommonAPI.post(user, %{status: "test", visibility: "local"}) + + assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post) + + object = Object.normalize(post, fetch: false) + uuid = String.split(object.data["id"], "/") |> List.last() + + assert response = + conn + |> assign(:user, user) + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + + assert json_response(response, 200) == ObjectView.render("object.json", %{object: object}) + end + test "it returns a json representation of the object with accept application/json", %{ conn: conn } do @@ -286,6 +303,28 @@ test "it returns 404 for non-public messages", %{conn: conn} do assert json_response(conn, 404) end + test "returns visible non-public messages when authenticated", %{conn: conn} do + note = insert(:direct_note) + uuid = String.split(note.data["id"], "/") |> List.last() + user = User.get_by_ap_id(note.data["actor"]) + marisa = insert(:user) + + assert conn + |> assign(:user, marisa) + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + |> json_response(404) + + assert response = + conn + |> assign(:user, user) + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + |> json_response(200) + + assert response == ObjectView.render("object.json", %{object: note}) + end + test "it returns 404 for tombstone objects", %{conn: conn} do tombstone = insert(:tombstone) uuid = String.split(tombstone.data["id"], "/") |> List.last() @@ -359,6 +398,23 @@ test "it doesn't return a local-only activity", %{conn: conn} do assert json_response(conn, 404) end + test "returns local-only activities when authenticated", %{conn: conn} do + user = insert(:user) + {:ok, post} = CommonAPI.post(user, %{status: "test", visibility: "local"}) + + assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post) + + uuid = String.split(post.data["id"], "/") |> List.last() + + assert response = + conn + |> assign(:user, user) + |> put_req_header("accept", "application/activity+json") + |> get("/activities/#{uuid}") + + assert json_response(response, 200) == ObjectView.render("object.json", %{object: post}) + end + test "it returns a json representation of the activity", %{conn: conn} do activity = insert(:note_activity) uuid = String.split(activity.data["id"], "/") |> List.last() @@ -383,6 +439,28 @@ test "it returns 404 for non-public activities", %{conn: conn} do assert json_response(conn, 404) end + test "returns visible non-public messages when authenticated", %{conn: conn} do + note = insert(:direct_note_activity) + uuid = String.split(note.data["id"], "/") |> List.last() + user = User.get_by_ap_id(note.data["actor"]) + marisa = insert(:user) + + assert conn + |> assign(:user, marisa) + |> put_req_header("accept", "application/activity+json") + |> get("/activities/#{uuid}") + |> json_response(404) + + assert response = + conn + |> assign(:user, user) + |> put_req_header("accept", "application/activity+json") + |> get("/activities/#{uuid}") + |> json_response(200) + + assert response == ObjectView.render("object.json", %{object: note}) + end + test "it caches a response", %{conn: conn} do activity = insert(:note_activity) uuid = String.split(activity.data["id"], "/") |> List.last() @@ -493,7 +571,7 @@ test "it clears `unreachable` federation status of the sender", %{conn: conn} do end test "accept follow activity", %{conn: conn} do - Pleroma.Config.put([:instance, :federating], true) + clear_config([:instance, :federating], true) relay = Relay.get_actor() assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor") @@ -539,7 +617,7 @@ test "without valid signature, " <> conn = put_req_header(conn, "content-type", "application/activity+json") - Config.put([:instance, :federating], false) + clear_config([:instance, :federating], false) conn |> post("/inbox", data) @@ -549,7 +627,7 @@ test "without valid signature, " <> |> post("/inbox", non_create_data) |> json_response(403) - Config.put([:instance, :federating], true) + clear_config([:instance, :federating], true) ret_conn = post(conn, "/inbox", data) assert "ok" == json_response(ret_conn, 200) @@ -558,6 +636,186 @@ test "without valid signature, " <> |> post("/inbox", non_create_data) |> json_response(400) end + + test "accepts Add/Remove activities", %{conn: conn} do + object_id = "c61d6733-e256-4fe1-ab13-1e369789423f" + + status = + File.read!("test/fixtures/statuses/note.json") + |> String.replace("{{nickname}}", "lain") + |> String.replace("{{object_id}}", object_id) + + object_url = "https://example.com/objects/#{object_id}" + + user = + File.read!("test/fixtures/users_mock/user.json") + |> String.replace("{{nickname}}", "lain") + + actor = "https://example.com/users/lain" + + Tesla.Mock.mock(fn + %{ + method: :get, + url: ^object_url + } -> + %Tesla.Env{ + status: 200, + body: status, + headers: [{"content-type", "application/activity+json"}] + } + + %{ + method: :get, + url: ^actor + } -> + %Tesla.Env{ + status: 200, + body: user, + headers: [{"content-type", "application/activity+json"}] + } + + %{method: :get, url: "https://example.com/users/lain/collections/featured"} -> + %Tesla.Env{ + status: 200, + body: + "test/fixtures/users_mock/masto_featured.json" + |> File.read!() + |> String.replace("{{domain}}", "example.com") + |> String.replace("{{nickname}}", "lain"), + headers: [{"content-type", "application/activity+json"}] + } + end) + + data = %{ + "id" => "https://example.com/objects/d61d6733-e256-4fe1-ab13-1e369789423f", + "actor" => actor, + "object" => object_url, + "target" => "https://example.com/users/lain/collections/featured", + "type" => "Add", + "to" => [Pleroma.Constants.as_public()] + } + + assert "ok" == + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/inbox", data) + |> json_response(200) + + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + assert Activity.get_by_ap_id(data["id"]) + user = User.get_cached_by_ap_id(data["actor"]) + assert user.pinned_objects[data["object"]] + + data = %{ + "id" => "https://example.com/objects/d61d6733-e256-4fe1-ab13-1e369789423d", + "actor" => actor, + "object" => object_url, + "target" => "https://example.com/users/lain/collections/featured", + "type" => "Remove", + "to" => [Pleroma.Constants.as_public()] + } + + assert "ok" == + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/inbox", data) + |> json_response(200) + + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + user = refresh_record(user) + refute user.pinned_objects[data["object"]] + end + + test "mastodon pin/unpin", %{conn: conn} do + status_id = "105786274556060421" + + status = + File.read!("test/fixtures/statuses/masto-note.json") + |> String.replace("{{nickname}}", "lain") + |> String.replace("{{status_id}}", status_id) + + status_url = "https://example.com/users/lain/statuses/#{status_id}" + + user = + File.read!("test/fixtures/users_mock/user.json") + |> String.replace("{{nickname}}", "lain") + + actor = "https://example.com/users/lain" + + Tesla.Mock.mock(fn + %{ + method: :get, + url: ^status_url + } -> + %Tesla.Env{ + status: 200, + body: status, + headers: [{"content-type", "application/activity+json"}] + } + + %{ + method: :get, + url: ^actor + } -> + %Tesla.Env{ + status: 200, + body: user, + headers: [{"content-type", "application/activity+json"}] + } + + %{method: :get, url: "https://example.com/users/lain/collections/featured"} -> + %Tesla.Env{ + status: 200, + body: + "test/fixtures/users_mock/masto_featured.json" + |> File.read!() + |> String.replace("{{domain}}", "example.com") + |> String.replace("{{nickname}}", "lain"), + headers: [{"content-type", "application/activity+json"}] + } + end) + + data = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "actor" => actor, + "object" => status_url, + "target" => "https://example.com/users/lain/collections/featured", + "type" => "Add" + } + + assert "ok" == + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/inbox", data) + |> json_response(200) + + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + assert Activity.get_by_object_ap_id_with_object(data["object"]) + user = User.get_cached_by_ap_id(data["actor"]) + assert user.pinned_objects[data["object"]] + + data = %{ + "actor" => actor, + "object" => status_url, + "target" => "https://example.com/users/lain/collections/featured", + "type" => "Remove" + } + + assert "ok" == + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/inbox", data) + |> json_response(200) + + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + assert Activity.get_by_object_ap_id_with_object(data["object"]) + user = refresh_record(user) + refute user.pinned_objects[data["object"]] + end end describe "/users/:nickname/inbox" do @@ -712,7 +970,7 @@ test "it rejects reads from other users", %{conn: conn} do test "it returns a note activity in a collection", %{conn: conn} do note_activity = insert(:direct_note_activity) - note_object = Object.normalize(note_activity) + note_object = Object.normalize(note_activity, fetch: false) user = User.get_cached_by_ap_id(hd(note_activity.data["to"])) conn = @@ -999,7 +1257,7 @@ test "it returns 200 even if there're no activities", %{conn: conn} do test "it returns a note activity in a collection", %{conn: conn} do note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) + note_object = Object.normalize(note_activity, fetch: false) user = User.get_cached_by_ap_id(note_activity.data["actor"]) conn = @@ -1023,6 +1281,31 @@ test "it returns an announce activity in a collection", %{conn: conn} do assert response(conn, 200) =~ announce_activity.data["object"] end + + test "It returns poll Answers when authenticated", %{conn: conn} do + poller = insert(:user) + voter = insert(:user) + + {:ok, activity} = + CommonAPI.post(poller, %{ + status: "suya...", + poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} + }) + + assert question = Object.normalize(activity, fetch: false) + + {:ok, [activity], _object} = CommonAPI.vote(voter, question, [1]) + + assert outbox_get = + conn + |> assign(:user, voter) + |> put_req_header("accept", "application/activity+json") + |> get(voter.ap_id <> "/outbox?page=true") + |> json_response(200) + + assert [answer_outbox] = outbox_get["orderedItems"] + assert answer_outbox["id"] == activity.data["id"] + end end describe "POST /users/:nickname/outbox (C2S)" do @@ -1073,7 +1356,7 @@ test "it inserts an incoming create activity into the database", %{ assert Activity.get_by_ap_id(result["id"]) assert result["object"] - assert %Object{data: object} = Object.normalize(result["object"]) + assert %Object{data: object} = Object.normalize(result["object"], fetch: false) assert object["content"] == activity["object"]["content"] end @@ -1109,7 +1392,7 @@ test "it inserts an incoming sensitive activity into the database", %{ assert Activity.get_by_ap_id(response["id"]) assert response["object"] - assert %Object{data: response_object} = Object.normalize(response["object"]) + assert %Object{data: response_object} = Object.normalize(response["object"], fetch: false) assert response_object["sensitive"] == true assert response_object["content"] == activity["object"]["content"] @@ -1137,7 +1420,7 @@ test "it rejects an incoming activity with bogus type", %{conn: conn, activity: test "it erects a tombstone when receiving a delete activity", %{conn: conn} do note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) + note_object = Object.normalize(note_activity, fetch: false) user = User.get_cached_by_ap_id(note_activity.data["actor"]) data = %{ @@ -1162,7 +1445,7 @@ test "it erects a tombstone when receiving a delete activity", %{conn: conn} do test "it rejects delete activity of object from other actor", %{conn: conn} do note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) + note_object = Object.normalize(note_activity, fetch: false) user = insert(:user) data = %{ @@ -1183,7 +1466,7 @@ test "it rejects delete activity of object from other actor", %{conn: conn} do test "it increases like count when receiving a like action", %{conn: conn} do note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) + note_object = Object.normalize(note_activity, fetch: false) user = User.get_cached_by_ap_id(note_activity.data["actor"]) data = %{ @@ -1240,13 +1523,13 @@ test "it doesn't spreads faulty attributedTo or actor fields", %{ assert cirno_outbox["attributedTo"] == nil assert cirno_outbox["actor"] == cirno.ap_id - assert cirno_object = Object.normalize(cirno_outbox["object"]) + assert cirno_object = Object.normalize(cirno_outbox["object"], fetch: false) assert cirno_object.data["actor"] == cirno.ap_id assert cirno_object.data["attributedTo"] == cirno.ap_id end test "Character limitation", %{conn: conn, activity: activity} do - Pleroma.Config.put([:instance, :limit], 5) + clear_config([:instance, :limit], 5) user = insert(:user) result = @@ -1275,7 +1558,7 @@ test "it returns relay followers", %{conn: conn} do end test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) + clear_config([:instance, :federating], false) user = insert(:user) conn @@ -1296,7 +1579,7 @@ test "it returns relay following", %{conn: conn} do end test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) + clear_config([:instance, :federating], false) user = insert(:user) conn @@ -1503,7 +1786,7 @@ test "does not require authentication", %{conn: conn} do test "it tracks a signed object fetch", %{conn: conn} do user = insert(:user, local: false) activity = insert(:note_activity) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) @@ -1519,7 +1802,7 @@ test "it tracks a signed object fetch", %{conn: conn} do test "it tracks a signed activity fetch", %{conn: conn} do user = insert(:user, local: false) activity = insert(:note_activity) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url()) @@ -1536,7 +1819,7 @@ test "it tracks a signed object fetch when the json is cached", %{conn: conn} do user = insert(:user, local: false) other_user = insert(:user, local: false) activity = insert(:note_activity) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) @@ -1560,7 +1843,7 @@ test "it tracks a signed activity fetch when the json is cached", %{conn: conn} user = insert(:user, local: false) other_user = insert(:user, local: false) activity = insert(:note_activity) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url()) @@ -1608,9 +1891,9 @@ test "POST /api/ap/upload_media", %{conn: conn} do desc = "Description of the image" image = %Plug.Upload{ - content_type: "bad/content-type", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.png" + filename: "an_image.jpg" } object = @@ -1650,7 +1933,7 @@ test "POST /api/ap/upload_media", %{conn: conn} do assert activity_response["actor"] == user.ap_id assert %Object{data: %{"attachment" => [attachment]}} = - Object.normalize(activity_response["object"]) + Object.normalize(activity_response["object"], fetch: false) assert attachment["type"] == "Document" assert attachment["name"] == desc @@ -1669,4 +1952,29 @@ test "POST /api/ap/upload_media", %{conn: conn} do |> json_response(403) end end + + test "pinned collection", %{conn: conn} do + clear_config([:instance, :max_pinned_statuses], 2) + user = insert(:user) + objects = insert_list(2, :note, user: user) + + Enum.reduce(objects, user, fn %{data: %{"id" => object_id}}, user -> + {:ok, updated} = User.add_pinned_object_id(user, object_id) + updated + end) + + %{nickname: nickname, featured_address: featured_address, pinned_objects: pinned_objects} = + refresh_record(user) + + %{"id" => ^featured_address, "orderedItems" => items} = + conn + |> get("/users/#{nickname}/collections/featured") + |> json_response(200) + + object_ids = Enum.map(items, & &1["id"]) + + assert Enum.all?(pinned_objects, fn {obj_id, _} -> + obj_id in object_ids + end) + end end diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 9eb7ae86b..64e12066e 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ActivityPubTest do @@ -190,37 +190,191 @@ test "it returns a user that accepts chat messages" do assert user.accepts_chat_messages end + + test "works for guppe actors" do + user_id = "https://gup.pe/u/bernie2020" + + Tesla.Mock.mock(fn + %{method: :get, url: ^user_id} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/guppe-actor.json"), + headers: [{"content-type", "application/activity+json"}] + } + end) + + {:ok, user} = ActivityPub.make_user_from_ap_id(user_id) + + assert user.name == "Bernie2020 group" + assert user.actor_type == "Group" + end + + test "works for bridgy actors" do + user_id = "https://fed.brid.gy/jk.nipponalba.scot" + + Tesla.Mock.mock(fn + %{method: :get, url: ^user_id} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/bridgy/actor.json"), + headers: [{"content-type", "application/activity+json"}] + } + end) + + {:ok, user} = ActivityPub.make_user_from_ap_id(user_id) + + assert user.actor_type == "Person" + + assert user.avatar == %{ + "type" => "Image", + "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}] + } + + assert user.banner == %{ + "type" => "Image", + "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}] + } + end + + test "fetches user featured collection" do + ap_id = "https://example.com/users/lain" + + featured_url = "https://example.com/users/lain/collections/featured" + + user_data = + "test/fixtures/users_mock/user.json" + |> File.read!() + |> String.replace("{{nickname}}", "lain") + |> Jason.decode!() + |> Map.put("featured", featured_url) + |> Jason.encode!() + + object_id = Ecto.UUID.generate() + + featured_data = + "test/fixtures/mastodon/collections/featured.json" + |> File.read!() + |> String.replace("{{domain}}", "example.com") + |> String.replace("{{nickname}}", "lain") + |> String.replace("{{object_id}}", object_id) + + object_url = "https://example.com/objects/#{object_id}" + + object_data = + "test/fixtures/statuses/note.json" + |> File.read!() + |> String.replace("{{object_id}}", object_id) + |> String.replace("{{nickname}}", "lain") + + Tesla.Mock.mock(fn + %{ + method: :get, + url: ^ap_id + } -> + %Tesla.Env{ + status: 200, + body: user_data, + headers: [{"content-type", "application/activity+json"}] + } + + %{ + method: :get, + url: ^featured_url + } -> + %Tesla.Env{ + status: 200, + body: featured_data, + headers: [{"content-type", "application/activity+json"}] + } + end) + + Tesla.Mock.mock_global(fn + %{ + method: :get, + url: ^object_url + } -> + %Tesla.Env{ + status: 200, + body: object_data, + headers: [{"content-type", "application/activity+json"}] + } + end) + + {:ok, user} = ActivityPub.make_user_from_ap_id(ap_id) + Process.sleep(50) + + assert user.featured_address == featured_url + assert Map.has_key?(user.pinned_objects, object_url) + + in_db = Pleroma.User.get_by_ap_id(ap_id) + assert in_db.featured_address == featured_url + assert Map.has_key?(user.pinned_objects, object_url) + + assert %{data: %{"id" => ^object_url}} = Object.get_by_ap_id(object_url) + end end test "it fetches the appropriate tag-restricted posts" do user = insert(:user) - {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"}) + {:ok, status_one} = CommonAPI.post(user, %{status: ". #TEST"}) {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"}) - {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"}) + {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #Reject"}) - fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"}) + {:ok, status_four} = CommonAPI.post(user, %{status: ". #Any1 #any2"}) + {:ok, status_five} = CommonAPI.post(user, %{status: ". #Any2 #any1"}) - fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]}) + for hashtag_timeline_strategy <- [:enabled, :disabled] do + clear_config([:features, :improved_hashtag_timeline], hashtag_timeline_strategy) - fetch_three = - ActivityPub.fetch_activities([], %{ - type: "Create", - tag: ["test", "essais"], - tag_reject: ["reject"] - }) + fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"}) - fetch_four = - ActivityPub.fetch_activities([], %{ - type: "Create", - tag: ["test"], - tag_all: ["test", "reject"] - }) + fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["TEST", "essais"]}) - assert fetch_one == [status_one, status_three] - assert fetch_two == [status_one, status_two, status_three] - assert fetch_three == [status_one, status_two] - assert fetch_four == [status_three] + fetch_three = + ActivityPub.fetch_activities([], %{ + type: "Create", + tag: ["test", "Essais"], + tag_reject: ["reject"] + }) + + fetch_four = + ActivityPub.fetch_activities([], %{ + type: "Create", + tag: ["test"], + tag_all: ["test", "REJECT"] + }) + + # Testing that deduplication (if needed) is done on DB (not Ecto) level; :limit is important + fetch_five = + ActivityPub.fetch_activities([], %{ + type: "Create", + tag: ["ANY1", "any2"], + limit: 2 + }) + + fetch_six = + ActivityPub.fetch_activities([], %{ + type: "Create", + tag: ["any1", "Any2"], + tag_all: [], + tag_reject: [] + }) + + # Regression test: passing empty lists as filter options shouldn't affect the results + assert fetch_five == fetch_six + + [fetch_one, fetch_two, fetch_three, fetch_four, fetch_five] = + Enum.map([fetch_one, fetch_two, fetch_three, fetch_four, fetch_five], fn statuses -> + Enum.map(statuses, fn s -> Repo.preload(s, object: :hashtags) end) + end) + + assert fetch_one == [status_one, status_three] + assert fetch_two == [status_one, status_two, status_three] + assert fetch_three == [status_one, status_two] + assert fetch_four == [status_three] + assert fetch_five == [status_four, status_five] + end end describe "insertion" do @@ -321,7 +475,7 @@ test "adds a context when none is there" do } {:ok, %Activity{} = activity} = ActivityPub.insert(data) - object = Pleroma.Object.normalize(activity) + object = Pleroma.Object.normalize(activity, fetch: false) assert is_binary(activity.data["context"]) assert is_binary(object.data["context"]) @@ -344,7 +498,7 @@ test "adds an id to a given object if it lacks one and is a note and inserts it } {:ok, %Activity{} = activity} = ActivityPub.insert(data) - assert object = Object.normalize(activity) + assert object = Object.normalize(activity, fetch: false) assert is_binary(object.data["id"]) end end @@ -678,7 +832,7 @@ test "doesn't return announce activities with blocked users in 'cc'" do {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"}) - assert object = Pleroma.Object.normalize(activity_two) + assert object = Pleroma.Object.normalize(activity_two, fetch: false) data = %{ "actor" => friend.ap_id, @@ -1061,15 +1215,15 @@ test "sets a description if given", %{test_file: file} do test "it sets the default description depending on the configuration", %{test_file: file} do clear_config([Pleroma.Upload, :default_description]) - Pleroma.Config.put([Pleroma.Upload, :default_description], nil) + clear_config([Pleroma.Upload, :default_description], nil) {:ok, %Object{} = object} = ActivityPub.upload(file) assert object.data["name"] == "" - Pleroma.Config.put([Pleroma.Upload, :default_description], :filename) + clear_config([Pleroma.Upload, :default_description], :filename) {:ok, %Object{} = object} = ActivityPub.upload(file) assert object.data["name"] == "an_image.jpg" - Pleroma.Config.put([Pleroma.Upload, :default_description], "unnamed attachment") + clear_config([Pleroma.Upload, :default_description], "unnamed attachment") {:ok, %Object{} = object} = ActivityPub.upload(file) assert object.data["name"] == "unnamed attachment" end diff --git a/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs b/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs index e7370d4ef..47b07fdd9 100644 --- a/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do diff --git a/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs b/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs index 3c795f5ac..d5af3a9b6 100644 --- a/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy diff --git a/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs b/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs index 6867c9853..5b990451c 100644 --- a/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do diff --git a/test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs b/test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs index 9a283f27d..89439b65f 100644 --- a/test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs +++ b/test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Activity alias Pleroma.Object diff --git a/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs b/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs new file mode 100644 index 000000000..a61562558 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs @@ -0,0 +1,126 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicyTest do + use Pleroma.DataCase, async: true + + alias Pleroma.User + alias Pleroma.Web.ActivityPub.MRF.FollowBotPolicy + + import Pleroma.Factory + + describe "FollowBotPolicy" do + test "follows remote users" do + bot = insert(:user, actor_type: "Service") + remote_user = insert(:user, local: false) + clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => [remote_user.follower_address], + "cc" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create", + "object" => %{ + "content" => "Test post", + "type" => "Note", + "attributedTo" => remote_user.ap_id, + "inReplyTo" => nil + }, + "actor" => remote_user.ap_id + } + + refute User.following?(bot, remote_user) + + assert User.get_follow_requests(remote_user) |> length == 0 + + FollowBotPolicy.filter(message) + + assert User.get_follow_requests(remote_user) |> length == 1 + end + + test "does not follow users with #nobot in bio" do + bot = insert(:user, actor_type: "Service") + remote_user = insert(:user, %{local: false, bio: "go away bots! #nobot"}) + clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => [remote_user.follower_address], + "cc" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create", + "object" => %{ + "content" => "I don't like follow bots", + "type" => "Note", + "attributedTo" => remote_user.ap_id, + "inReplyTo" => nil + }, + "actor" => remote_user.ap_id + } + + refute User.following?(bot, remote_user) + + assert User.get_follow_requests(remote_user) |> length == 0 + + FollowBotPolicy.filter(message) + + assert User.get_follow_requests(remote_user) |> length == 0 + end + + test "does not follow local users" do + bot = insert(:user, actor_type: "Service") + local_user = insert(:user, local: true) + clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => [local_user.follower_address], + "cc" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create", + "object" => %{ + "content" => "Hi I'm a local user", + "type" => "Note", + "attributedTo" => local_user.ap_id, + "inReplyTo" => nil + }, + "actor" => local_user.ap_id + } + + refute User.following?(bot, local_user) + + assert User.get_follow_requests(local_user) |> length == 0 + + FollowBotPolicy.filter(message) + + assert User.get_follow_requests(local_user) |> length == 0 + end + + test "does not follow users requiring follower approval" do + bot = insert(:user, actor_type: "Service") + remote_user = insert(:user, %{local: false, is_locked: true}) + clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => [remote_user.follower_address], + "cc" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create", + "object" => %{ + "content" => "I don't like randos following me", + "type" => "Note", + "attributedTo" => remote_user.ap_id, + "inReplyTo" => nil + }, + "actor" => remote_user.ap_id + } + + refute User.following?(bot, remote_user) + + assert User.get_follow_requests(remote_user) |> length == 0 + + FollowBotPolicy.filter(message) + + assert User.get_follow_requests(remote_user) |> length == 0 + end + end +end diff --git a/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs index 86dd9ddae..e3325d144 100644 --- a/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicyTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy diff --git a/test/pleroma/web/activity_pub/mrf/hashtag_policy_test.exs b/test/pleroma/web/activity_pub/mrf/hashtag_policy_test.exs new file mode 100644 index 000000000..13415bb79 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/hashtag_policy_test.exs @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicyTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it sets the sensitive property with relevant hashtags" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"}) + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert modified["object"]["sensitive"] + end + + test "it doesn't sets the sensitive property with irrelevant hashtags" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe hey"}) + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + refute modified["object"]["sensitive"] + end +end diff --git a/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs b/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs index 26f5bcdaa..439672479 100644 --- a/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do @@ -34,7 +34,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do setup do: clear_config(:mrf_hellthread) test "doesn't die on chat messages" do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) + clear_config([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) user = insert(:user) other_user = insert(:user) @@ -48,7 +48,7 @@ test "doesn't die on chat messages" do test "rejects the message if the recipient count is above reject_threshold", %{ message: message } do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 2}) + clear_config([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 2}) assert {:reject, "[HellthreadPolicy] 3 recipients is over the limit of 2"} == filter(message) @@ -57,7 +57,7 @@ test "rejects the message if the recipient count is above reject_threshold", %{ test "does not reject the message if the recipient count is below reject_threshold", %{ message: message } do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3}) + clear_config([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3}) assert {:ok, ^message} = filter(message) end @@ -68,7 +68,7 @@ test "delists the message if the recipient count is above delist_threshold", %{ user: user, message: message } do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) + clear_config([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) {:ok, message} = filter(message) assert user.follower_address in message["to"] @@ -78,14 +78,14 @@ test "delists the message if the recipient count is above delist_threshold", %{ test "does not delist the message if the recipient count is below delist_threshold", %{ message: message } do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 4, reject_threshold: 0}) + clear_config([:mrf_hellthread], %{delist_threshold: 4, reject_threshold: 0}) assert {:ok, ^message} = filter(message) end end test "excludes follower collection and public URI from threshold count", %{message: message} do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3}) + clear_config([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3}) assert {:ok, ^message} = filter(message) end diff --git a/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs b/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs index b3d0f3d90..8af4c5efa 100644 --- a/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do @@ -10,12 +10,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do setup do: clear_config(:mrf_keyword) setup do - Pleroma.Config.put([:mrf_keyword], %{reject: [], federated_timeline_removal: [], replace: []}) + clear_config([:mrf_keyword], %{reject: [], federated_timeline_removal: [], replace: []}) end describe "rejecting based on keywords" do test "rejects if string matches in content" do - Pleroma.Config.put([:mrf_keyword, :reject], ["pun"]) + clear_config([:mrf_keyword, :reject], ["pun"]) message = %{ "type" => "Create", @@ -30,7 +30,7 @@ test "rejects if string matches in content" do end test "rejects if string matches in summary" do - Pleroma.Config.put([:mrf_keyword, :reject], ["pun"]) + clear_config([:mrf_keyword, :reject], ["pun"]) message = %{ "type" => "Create", @@ -45,7 +45,7 @@ test "rejects if string matches in summary" do end test "rejects if regex matches in content" do - Pleroma.Config.put([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/]) + clear_config([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/]) assert true == Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> @@ -63,7 +63,7 @@ test "rejects if regex matches in content" do end test "rejects if regex matches in summary" do - Pleroma.Config.put([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/]) + clear_config([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/]) assert true == Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> @@ -83,7 +83,7 @@ test "rejects if regex matches in summary" do describe "delisting from ftl based on keywords" do test "delists if string matches in content" do - Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], ["pun"]) + clear_config([:mrf_keyword, :federated_timeline_removal], ["pun"]) message = %{ "to" => ["https://www.w3.org/ns/activitystreams#Public"], @@ -100,7 +100,7 @@ test "delists if string matches in content" do end test "delists if string matches in summary" do - Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], ["pun"]) + clear_config([:mrf_keyword, :federated_timeline_removal], ["pun"]) message = %{ "to" => ["https://www.w3.org/ns/activitystreams#Public"], @@ -117,7 +117,7 @@ test "delists if string matches in summary" do end test "delists if regex matches in content" do - Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/]) + clear_config([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/]) assert true == Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> @@ -138,7 +138,7 @@ test "delists if regex matches in content" do end test "delists if regex matches in summary" do - Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/]) + clear_config([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/]) assert true == Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> @@ -161,7 +161,7 @@ test "delists if regex matches in summary" do describe "replacing keywords" do test "replaces keyword if string matches in content" do - Pleroma.Config.put([:mrf_keyword, :replace], [{"opensource", "free software"}]) + clear_config([:mrf_keyword, :replace], [{"opensource", "free software"}]) message = %{ "type" => "Create", @@ -174,7 +174,7 @@ test "replaces keyword if string matches in content" do end test "replaces keyword if string matches in summary" do - Pleroma.Config.put([:mrf_keyword, :replace], [{"opensource", "free software"}]) + clear_config([:mrf_keyword, :replace], [{"opensource", "free software"}]) message = %{ "type" => "Create", @@ -187,7 +187,7 @@ test "replaces keyword if string matches in summary" do end test "replaces keyword if regex matches in content" do - Pleroma.Config.put([:mrf_keyword, :replace], [ + clear_config([:mrf_keyword, :replace], [ {~r/open(-|\s)?source\s?(software)?/, "free software"} ]) @@ -205,7 +205,7 @@ test "replaces keyword if regex matches in content" do end test "replaces keyword if regex matches in summary" do - Pleroma.Config.put([:mrf_keyword, :replace], [ + clear_config([:mrf_keyword, :replace], [ {~r/open(-|\s)?source\s?(software)?/, "free software"} ]) diff --git a/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs index 84362ce78..96e715d0d 100644 --- a/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do diff --git a/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs b/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs index 220309cc9..80ddcacbe 100644 --- a/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicyTest do @@ -23,7 +23,7 @@ test "pass filter if allow list is empty" do describe "allow" do test "empty" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) + clear_config([:mrf_mention], %{actors: ["https://example.com/blocked"]}) message = %{ "type" => "Create" @@ -33,7 +33,7 @@ test "empty" do end test "to" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) + clear_config([:mrf_mention], %{actors: ["https://example.com/blocked"]}) message = %{ "type" => "Create", @@ -44,7 +44,7 @@ test "to" do end test "cc" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) + clear_config([:mrf_mention], %{actors: ["https://example.com/blocked"]}) message = %{ "type" => "Create", @@ -55,7 +55,7 @@ test "cc" do end test "both" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) + clear_config([:mrf_mention], %{actors: ["https://example.com/blocked"]}) message = %{ "type" => "Create", @@ -69,7 +69,7 @@ test "both" do describe "deny" do test "to" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) + clear_config([:mrf_mention], %{actors: ["https://example.com/blocked"]}) message = %{ "type" => "Create", @@ -81,7 +81,7 @@ test "to" do end test "cc" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) + clear_config([:mrf_mention], %{actors: ["https://example.com/blocked"]}) message = %{ "type" => "Create", diff --git a/test/pleroma/web/activity_pub/mrf/no_empty_policy_test.exs b/test/pleroma/web/activity_pub/mrf/no_empty_policy_test.exs new file mode 100644 index 000000000..fbcf68414 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/no_empty_policy_test.exs @@ -0,0 +1,154 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicyTest do + use Pleroma.DataCase + alias Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy + + setup_all do: clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy]) + + test "Notes with content are exempt" do + message = %{ + "actor" => "http://localhost:4001/users/testuser", + "cc" => ["http://localhost:4001/users/testuser/followers"], + "object" => %{ + "actor" => "http://localhost:4001/users/testuser", + "attachment" => [], + "cc" => ["http://localhost:4001/users/testuser/followers"], + "source" => "this is a test post", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Note" + }, + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create" + } + + assert NoEmptyPolicy.filter(message) == {:ok, message} + end + + test "Polls are exempt" do + message = %{ + "actor" => "http://localhost:4001/users/testuser", + "cc" => ["http://localhost:4001/users/testuser/followers"], + "object" => %{ + "actor" => "http://localhost:4001/users/testuser", + "attachment" => [], + "cc" => ["http://localhost:4001/users/testuser/followers"], + "oneOf" => [ + %{ + "name" => "chocolate", + "replies" => %{"totalItems" => 0, "type" => "Collection"}, + "type" => "Note" + }, + %{ + "name" => "vanilla", + "replies" => %{"totalItems" => 0, "type" => "Collection"}, + "type" => "Note" + } + ], + "source" => "@user2", + "to" => [ + "https://www.w3.org/ns/activitystreams#Public", + "http://localhost:4001/users/user2" + ], + "type" => "Question" + }, + "to" => [ + "https://www.w3.org/ns/activitystreams#Public", + "http://localhost:4001/users/user2" + ], + "type" => "Create" + } + + assert NoEmptyPolicy.filter(message) == {:ok, message} + end + + test "Notes with attachments are exempt" do + message = %{ + "actor" => "http://localhost:4001/users/testuser", + "cc" => ["http://localhost:4001/users/testuser/followers"], + "object" => %{ + "actor" => "http://localhost:4001/users/testuser", + "attachment" => [ + %{ + "actor" => "http://localhost:4001/users/testuser", + "mediaType" => "image/png", + "name" => "", + "type" => "Document", + "url" => [ + %{ + "href" => + "http://localhost:4001/media/68ba231cf12e1382ce458f1979969f8ed5cc07ba198a02e653464abaf39bdb90.png", + "mediaType" => "image/png", + "type" => "Link" + } + ] + } + ], + "cc" => ["http://localhost:4001/users/testuser/followers"], + "source" => "@user2", + "to" => [ + "https://www.w3.org/ns/activitystreams#Public", + "http://localhost:4001/users/user2" + ], + "type" => "Note" + }, + "to" => [ + "https://www.w3.org/ns/activitystreams#Public", + "http://localhost:4001/users/user2" + ], + "type" => "Create" + } + + assert NoEmptyPolicy.filter(message) == {:ok, message} + end + + test "Notes with only mentions are denied" do + message = %{ + "actor" => "http://localhost:4001/users/testuser", + "cc" => ["http://localhost:4001/users/testuser/followers"], + "object" => %{ + "actor" => "http://localhost:4001/users/testuser", + "attachment" => [], + "cc" => ["http://localhost:4001/users/testuser/followers"], + "source" => "@user2", + "to" => [ + "https://www.w3.org/ns/activitystreams#Public", + "http://localhost:4001/users/user2" + ], + "type" => "Note" + }, + "to" => [ + "https://www.w3.org/ns/activitystreams#Public", + "http://localhost:4001/users/user2" + ], + "type" => "Create" + } + + assert NoEmptyPolicy.filter(message) == {:reject, "[NoEmptyPolicy]"} + end + + test "Notes with no content are denied" do + message = %{ + "actor" => "http://localhost:4001/users/testuser", + "cc" => ["http://localhost:4001/users/testuser/followers"], + "object" => %{ + "actor" => "http://localhost:4001/users/testuser", + "attachment" => [], + "cc" => ["http://localhost:4001/users/testuser/followers"], + "source" => "", + "to" => [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "type" => "Note" + }, + "to" => [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "type" => "Create" + } + + assert NoEmptyPolicy.filter(message) == {:reject, "[NoEmptyPolicy]"} + end +end diff --git a/test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs b/test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs index 64ea61dd4..81a6e0f50 100644 --- a/test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicyTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy test "it clears content object" do diff --git a/test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs b/test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs index 9b39c45bd..edc330b6c 100644 --- a/test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs +++ b/test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkupTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.ActivityPub.MRF.NormalizeMarkup @html_sample """ diff --git a/test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs b/test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs index e8317b2af..137aafd39 100644 --- a/test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs @@ -1,10 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do use Pleroma.DataCase - alias Pleroma.Config alias Pleroma.User alias Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy alias Pleroma.Web.ActivityPub.Visibility @@ -39,7 +38,7 @@ defp get_new_message do describe "with reject action" do test "works with objects with empty to or cc fields" do - Config.put([:mrf_object_age, :actions], [:reject]) + clear_config([:mrf_object_age, :actions], [:reject]) data = get_old_message() @@ -50,7 +49,7 @@ test "works with objects with empty to or cc fields" do end test "it rejects an old post" do - Config.put([:mrf_object_age, :actions], [:reject]) + clear_config([:mrf_object_age, :actions], [:reject]) data = get_old_message() @@ -58,7 +57,7 @@ test "it rejects an old post" do end test "it allows a new post" do - Config.put([:mrf_object_age, :actions], [:reject]) + clear_config([:mrf_object_age, :actions], [:reject]) data = get_new_message() @@ -68,7 +67,7 @@ test "it allows a new post" do describe "with delist action" do test "works with objects with empty to or cc fields" do - Config.put([:mrf_object_age, :actions], [:delist]) + clear_config([:mrf_object_age, :actions], [:delist]) data = get_old_message() @@ -83,7 +82,7 @@ test "works with objects with empty to or cc fields" do end test "it delists an old post" do - Config.put([:mrf_object_age, :actions], [:delist]) + clear_config([:mrf_object_age, :actions], [:delist]) data = get_old_message() @@ -95,7 +94,7 @@ test "it delists an old post" do end test "it allows a new post" do - Config.put([:mrf_object_age, :actions], [:delist]) + clear_config([:mrf_object_age, :actions], [:delist]) data = get_new_message() @@ -107,7 +106,7 @@ test "it allows a new post" do describe "with strip_followers action" do test "works with objects with empty to or cc fields" do - Config.put([:mrf_object_age, :actions], [:strip_followers]) + clear_config([:mrf_object_age, :actions], [:strip_followers]) data = get_old_message() @@ -123,7 +122,7 @@ test "works with objects with empty to or cc fields" do end test "it strips followers collections from an old post" do - Config.put([:mrf_object_age, :actions], [:strip_followers]) + clear_config([:mrf_object_age, :actions], [:strip_followers]) data = get_old_message() @@ -136,7 +135,7 @@ test "it strips followers collections from an old post" do end test "it allows a new post" do - Config.put([:mrf_object_age, :actions], [:strip_followers]) + clear_config([:mrf_object_age, :actions], [:strip_followers]) data = get_new_message() diff --git a/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs b/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs index e08eb3ba6..63c68d798 100644 --- a/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs +++ b/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do @@ -49,7 +49,7 @@ test "it's allowed when addrer of message in the follower addresses of user and "type" => "Create" } - Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], true) + clear_config([:mrf_rejectnonpublic, :allow_followersonly], true) assert {:ok, _message} = RejectNonPublic.filter(message) end @@ -63,7 +63,7 @@ test "it's rejected when addrer of message in the follower addresses of user and "type" => "Create" } - Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], false) + clear_config([:mrf_rejectnonpublic, :allow_followersonly], false) assert {:reject, _} = RejectNonPublic.filter(message) end end @@ -79,7 +79,7 @@ test "it's allows when direct messages are allow" do "type" => "Create" } - Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], true) + clear_config([:mrf_rejectnonpublic, :allow_direct], true) assert {:ok, _message} = RejectNonPublic.filter(message) end @@ -93,7 +93,7 @@ test "it's reject when direct messages aren't allow" do "type" => "Create" } - Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], false) + clear_config([:mrf_rejectnonpublic, :allow_direct], false) assert {:reject, _} = RejectNonPublic.filter(message) end end diff --git a/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs index d7dde62c4..5c0aff26e 100644 --- a/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs @@ -1,11 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do use Pleroma.DataCase import Pleroma.Factory - alias Pleroma.Config alias Pleroma.Web.ActivityPub.MRF.SimplePolicy alias Pleroma.Web.CommonAPI @@ -25,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do describe "when :media_removal" do test "is empty" do - Config.put([:mrf_simple, :media_removal], []) + clear_config([:mrf_simple, :media_removal], []) media_message = build_media_message() local_message = build_local_message() @@ -34,7 +33,7 @@ test "is empty" do end test "has a matching host" do - Config.put([:mrf_simple, :media_removal], ["remote.instance"]) + clear_config([:mrf_simple, :media_removal], ["remote.instance"]) media_message = build_media_message() local_message = build_local_message() @@ -47,7 +46,7 @@ test "has a matching host" do end test "match with wildcard domain" do - Config.put([:mrf_simple, :media_removal], ["*.remote.instance"]) + clear_config([:mrf_simple, :media_removal], ["*.remote.instance"]) media_message = build_media_message() local_message = build_local_message() @@ -62,7 +61,7 @@ test "match with wildcard domain" do describe "when :media_nsfw" do test "is empty" do - Config.put([:mrf_simple, :media_nsfw], []) + clear_config([:mrf_simple, :media_nsfw], []) media_message = build_media_message() local_message = build_local_message() @@ -71,29 +70,23 @@ test "is empty" do end test "has a matching host" do - Config.put([:mrf_simple, :media_nsfw], ["remote.instance"]) + clear_config([:mrf_simple, :media_nsfw], ["remote.instance"]) media_message = build_media_message() local_message = build_local_message() assert SimplePolicy.filter(media_message) == - {:ok, - media_message - |> put_in(["object", "tag"], ["foo", "nsfw"]) - |> put_in(["object", "sensitive"], true)} + {:ok, put_in(media_message, ["object", "sensitive"], true)} assert SimplePolicy.filter(local_message) == {:ok, local_message} end test "match with wildcard domain" do - Config.put([:mrf_simple, :media_nsfw], ["*.remote.instance"]) + clear_config([:mrf_simple, :media_nsfw], ["*.remote.instance"]) media_message = build_media_message() local_message = build_local_message() assert SimplePolicy.filter(media_message) == - {:ok, - media_message - |> put_in(["object", "tag"], ["foo", "nsfw"]) - |> put_in(["object", "sensitive"], true)} + {:ok, put_in(media_message, ["object", "sensitive"], true)} assert SimplePolicy.filter(local_message) == {:ok, local_message} end @@ -113,7 +106,7 @@ defp build_media_message do describe "when :report_removal" do test "is empty" do - Config.put([:mrf_simple, :report_removal], []) + clear_config([:mrf_simple, :report_removal], []) report_message = build_report_message() local_message = build_local_message() @@ -122,7 +115,7 @@ test "is empty" do end test "has a matching host" do - Config.put([:mrf_simple, :report_removal], ["remote.instance"]) + clear_config([:mrf_simple, :report_removal], ["remote.instance"]) report_message = build_report_message() local_message = build_local_message() @@ -131,7 +124,7 @@ test "has a matching host" do end test "match with wildcard domain" do - Config.put([:mrf_simple, :report_removal], ["*.remote.instance"]) + clear_config([:mrf_simple, :report_removal], ["*.remote.instance"]) report_message = build_report_message() local_message = build_local_message() @@ -149,7 +142,7 @@ defp build_report_message do describe "when :federated_timeline_removal" do test "is empty" do - Config.put([:mrf_simple, :federated_timeline_removal], []) + clear_config([:mrf_simple, :federated_timeline_removal], []) {_, ftl_message} = build_ftl_actor_and_message() local_message = build_local_message() @@ -166,7 +159,7 @@ test "has a matching host" do |> URI.parse() |> Map.fetch!(:host) - Config.put([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host]) + clear_config([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host]) local_message = build_local_message() assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message) @@ -187,7 +180,7 @@ test "match with wildcard domain" do |> URI.parse() |> Map.fetch!(:host) - Config.put([:mrf_simple, :federated_timeline_removal], ["*." <> ftl_message_actor_host]) + clear_config([:mrf_simple, :federated_timeline_removal], ["*." <> ftl_message_actor_host]) local_message = build_local_message() assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message) @@ -210,7 +203,7 @@ test "has a matching host but only as:Public in to" do ftl_message = Map.put(ftl_message, "cc", []) - Config.put([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host]) + clear_config([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host]) assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message) refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"] @@ -231,7 +224,7 @@ defp build_ftl_actor_and_message do describe "when :reject" do test "is empty" do - Config.put([:mrf_simple, :reject], []) + clear_config([:mrf_simple, :reject], []) remote_message = build_remote_message() @@ -239,7 +232,7 @@ test "is empty" do end test "activity has a matching host" do - Config.put([:mrf_simple, :reject], ["remote.instance"]) + clear_config([:mrf_simple, :reject], ["remote.instance"]) remote_message = build_remote_message() @@ -247,7 +240,7 @@ test "activity has a matching host" do end test "activity matches with wildcard domain" do - Config.put([:mrf_simple, :reject], ["*.remote.instance"]) + clear_config([:mrf_simple, :reject], ["*.remote.instance"]) remote_message = build_remote_message() @@ -255,7 +248,7 @@ test "activity matches with wildcard domain" do end test "actor has a matching host" do - Config.put([:mrf_simple, :reject], ["remote.instance"]) + clear_config([:mrf_simple, :reject], ["remote.instance"]) remote_user = build_remote_user() @@ -265,7 +258,7 @@ test "actor has a matching host" do describe "when :followers_only" do test "is empty" do - Config.put([:mrf_simple, :followers_only], []) + clear_config([:mrf_simple, :followers_only], []) {_, ftl_message} = build_ftl_actor_and_message() local_message = build_local_message() @@ -305,7 +298,7 @@ test "has a matching host" do |> URI.parse() |> Map.fetch!(:host) - Config.put([:mrf_simple, :followers_only], [actor_domain]) + clear_config([:mrf_simple, :followers_only], [actor_domain]) assert {:ok, new_activity} = SimplePolicy.filter(activity) assert actor.follower_address in new_activity["cc"] @@ -323,7 +316,7 @@ test "has a matching host" do describe "when :accept" do test "is empty" do - Config.put([:mrf_simple, :accept], []) + clear_config([:mrf_simple, :accept], []) local_message = build_local_message() remote_message = build_remote_message() @@ -333,7 +326,7 @@ test "is empty" do end test "is not empty but activity doesn't have a matching host" do - Config.put([:mrf_simple, :accept], ["non.matching.remote"]) + clear_config([:mrf_simple, :accept], ["non.matching.remote"]) local_message = build_local_message() remote_message = build_remote_message() @@ -343,7 +336,7 @@ test "is not empty but activity doesn't have a matching host" do end test "activity has a matching host" do - Config.put([:mrf_simple, :accept], ["remote.instance"]) + clear_config([:mrf_simple, :accept], ["remote.instance"]) local_message = build_local_message() remote_message = build_remote_message() @@ -353,7 +346,7 @@ test "activity has a matching host" do end test "activity matches with wildcard domain" do - Config.put([:mrf_simple, :accept], ["*.remote.instance"]) + clear_config([:mrf_simple, :accept], ["*.remote.instance"]) local_message = build_local_message() remote_message = build_remote_message() @@ -363,7 +356,7 @@ test "activity matches with wildcard domain" do end test "actor has a matching host" do - Config.put([:mrf_simple, :accept], ["remote.instance"]) + clear_config([:mrf_simple, :accept], ["remote.instance"]) remote_user = build_remote_user() @@ -373,7 +366,7 @@ test "actor has a matching host" do describe "when :avatar_removal" do test "is empty" do - Config.put([:mrf_simple, :avatar_removal], []) + clear_config([:mrf_simple, :avatar_removal], []) remote_user = build_remote_user() @@ -381,7 +374,7 @@ test "is empty" do end test "is not empty but it doesn't have a matching host" do - Config.put([:mrf_simple, :avatar_removal], ["non.matching.remote"]) + clear_config([:mrf_simple, :avatar_removal], ["non.matching.remote"]) remote_user = build_remote_user() @@ -389,7 +382,7 @@ test "is not empty but it doesn't have a matching host" do end test "has a matching host" do - Config.put([:mrf_simple, :avatar_removal], ["remote.instance"]) + clear_config([:mrf_simple, :avatar_removal], ["remote.instance"]) remote_user = build_remote_user() {:ok, filtered} = SimplePolicy.filter(remote_user) @@ -398,7 +391,7 @@ test "has a matching host" do end test "match with wildcard domain" do - Config.put([:mrf_simple, :avatar_removal], ["*.remote.instance"]) + clear_config([:mrf_simple, :avatar_removal], ["*.remote.instance"]) remote_user = build_remote_user() {:ok, filtered} = SimplePolicy.filter(remote_user) @@ -409,7 +402,7 @@ test "match with wildcard domain" do describe "when :banner_removal" do test "is empty" do - Config.put([:mrf_simple, :banner_removal], []) + clear_config([:mrf_simple, :banner_removal], []) remote_user = build_remote_user() @@ -417,7 +410,7 @@ test "is empty" do end test "is not empty but it doesn't have a matching host" do - Config.put([:mrf_simple, :banner_removal], ["non.matching.remote"]) + clear_config([:mrf_simple, :banner_removal], ["non.matching.remote"]) remote_user = build_remote_user() @@ -425,7 +418,7 @@ test "is not empty but it doesn't have a matching host" do end test "has a matching host" do - Config.put([:mrf_simple, :banner_removal], ["remote.instance"]) + clear_config([:mrf_simple, :banner_removal], ["remote.instance"]) remote_user = build_remote_user() {:ok, filtered} = SimplePolicy.filter(remote_user) @@ -434,7 +427,7 @@ test "has a matching host" do end test "match with wildcard domain" do - Config.put([:mrf_simple, :banner_removal], ["*.remote.instance"]) + clear_config([:mrf_simple, :banner_removal], ["*.remote.instance"]) remote_user = build_remote_user() {:ok, filtered} = SimplePolicy.filter(remote_user) @@ -444,10 +437,10 @@ test "match with wildcard domain" do end describe "when :reject_deletes is empty" do - setup do: Config.put([:mrf_simple, :reject_deletes], []) + setup do: clear_config([:mrf_simple, :reject_deletes], []) test "it accepts deletions even from rejected servers" do - Config.put([:mrf_simple, :reject], ["remote.instance"]) + clear_config([:mrf_simple, :reject], ["remote.instance"]) deletion_message = build_remote_deletion_message() @@ -455,7 +448,7 @@ test "it accepts deletions even from rejected servers" do end test "it accepts deletions even from non-whitelisted servers" do - Config.put([:mrf_simple, :accept], ["non.matching.remote"]) + clear_config([:mrf_simple, :accept], ["non.matching.remote"]) deletion_message = build_remote_deletion_message() @@ -464,10 +457,10 @@ test "it accepts deletions even from non-whitelisted servers" do end describe "when :reject_deletes is not empty but it doesn't have a matching host" do - setup do: Config.put([:mrf_simple, :reject_deletes], ["non.matching.remote"]) + setup do: clear_config([:mrf_simple, :reject_deletes], ["non.matching.remote"]) test "it accepts deletions even from rejected servers" do - Config.put([:mrf_simple, :reject], ["remote.instance"]) + clear_config([:mrf_simple, :reject], ["remote.instance"]) deletion_message = build_remote_deletion_message() @@ -475,7 +468,7 @@ test "it accepts deletions even from rejected servers" do end test "it accepts deletions even from non-whitelisted servers" do - Config.put([:mrf_simple, :accept], ["non.matching.remote"]) + clear_config([:mrf_simple, :accept], ["non.matching.remote"]) deletion_message = build_remote_deletion_message() @@ -484,7 +477,7 @@ test "it accepts deletions even from non-whitelisted servers" do end describe "when :reject_deletes has a matching host" do - setup do: Config.put([:mrf_simple, :reject_deletes], ["remote.instance"]) + setup do: clear_config([:mrf_simple, :reject_deletes], ["remote.instance"]) test "it rejects the deletion" do deletion_message = build_remote_deletion_message() @@ -494,7 +487,7 @@ test "it rejects the deletion" do end describe "when :reject_deletes match with wildcard domain" do - setup do: Config.put([:mrf_simple, :reject_deletes], ["*.remote.instance"]) + setup do: clear_config([:mrf_simple, :reject_deletes], ["*.remote.instance"]) test "it rejects the deletion" do deletion_message = build_remote_deletion_message() diff --git a/test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs b/test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs index 3f8222736..bae57f29a 100644 --- a/test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs @@ -1,11 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicyTest do use Pleroma.DataCase alias Pleroma.Config + alias Pleroma.Emoji alias Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy setup_all do @@ -14,55 +15,91 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicyTest do end setup do - emoji_path = Path.join(Config.get([:instance, :static_dir]), "emoji/stolen") - File.rm_rf!(emoji_path) - File.mkdir!(emoji_path) + emoji_path = [:instance, :static_dir] |> Config.get() |> Path.join("emoji/stolen") - Pleroma.Emoji.reload() + Emoji.reload() + + message = %{ + "type" => "Create", + "object" => %{ + "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}], + "actor" => "https://example.org/users/admin" + } + } on_exit(fn -> File.rm_rf!(emoji_path) end) - :ok + [message: message, path: emoji_path] end - test "does nothing by default" do - installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - refute "firedfox" in installed_emoji + test "does nothing by default", %{message: message} do + refute "firedfox" in installed() - message = %{ - "type" => "Create", - "object" => %{ - "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}], - "actor" => "https://example.org/users/admin" - } - } + assert {:ok, _message} = StealEmojiPolicy.filter(message) - assert {:ok, message} == StealEmojiPolicy.filter(message) - - installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - refute "firedfox" in installed_emoji + refute "firedfox" in installed() end - test "Steals emoji on unknown shortcode from allowed remote host" do - installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - refute "firedfox" in installed_emoji + test "Steals emoji on unknown shortcode from allowed remote host", %{ + message: message, + path: path + } do + refute "firedfox" in installed() + refute File.exists?(path) - message = %{ - "type" => "Create", - "object" => %{ - "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}], - "actor" => "https://example.org/users/admin" - } - } + clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468) - clear_config([:mrf_steal_emoji, :hosts], ["example.org"]) - clear_config([:mrf_steal_emoji, :size_limit], 284_468) + assert {:ok, _message} = StealEmojiPolicy.filter(message) - assert {:ok, message} == StealEmojiPolicy.filter(message) + assert "firedfox" in installed() + assert File.exists?(path) - installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - assert "firedfox" in installed_emoji + assert path + |> Path.join("firedfox.png") + |> File.exists?() end + + test "reject shortcode", %{message: message} do + refute "firedfox" in installed() + + clear_config(:mrf_steal_emoji, + hosts: ["example.org"], + size_limit: 284_468, + rejected_shortcodes: [~r/firedfox/] + ) + + assert {:ok, _message} = StealEmojiPolicy.filter(message) + + refute "firedfox" in installed() + end + + test "reject if size is above the limit", %{message: message} do + refute "firedfox" in installed() + + clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 50_000) + + assert {:ok, _message} = StealEmojiPolicy.filter(message) + + refute "firedfox" in installed() + end + + test "reject if host returns error", %{message: message} do + refute "firedfox" in installed() + + Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox.png"} -> + {:ok, %Tesla.Env{status: 404, body: "Not found"}} + end) + + clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468) + + ExUnit.CaptureLog.capture_log(fn -> + assert {:ok, _message} = StealEmojiPolicy.filter(message) + end) =~ "MRF.StealEmojiPolicy: Failed to fetch https://example.org/emoji/firedfox.png" + + refute "firedfox" in installed() + end + + defp installed, do: Emoji.get_all() |> Enum.map(fn {k, _} -> k end) end diff --git a/test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs b/test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs index fff66cb7e..4f5cc466c 100644 --- a/test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicyTest do @@ -16,7 +16,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicyTest do setup do: clear_config([:mrf_subchain, :match_actor]) test "it matches and processes subchains when the actor matches a configured target" do - Pleroma.Config.put([:mrf_subchain, :match_actor], %{ + clear_config([:mrf_subchain, :match_actor], %{ ~r/^https:\/\/banned.com/s => [DropPolicy] }) @@ -24,7 +24,7 @@ test "it matches and processes subchains when the actor matches a configured tar end test "it doesn't match and process subchains when the actor doesn't match a configured target" do - Pleroma.Config.put([:mrf_subchain, :match_actor], %{ + clear_config([:mrf_subchain, :match_actor], %{ ~r/^https:\/\/borked.com/s => [DropPolicy] }) diff --git a/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs index ffc30ba62..faaadff79 100644 --- a/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Web.ActivityPub.MRF.TagPolicy @@ -114,7 +114,7 @@ test "Mark as sensitive on presence of attachments" do except_message = %{ "actor" => actor.ap_id, "type" => "Create", - "object" => %{"tag" => ["test", "nsfw"], "attachment" => ["file1"], "sensitive" => true} + "object" => %{"tag" => ["test"], "attachment" => ["file1"], "sensitive" => true} } assert TagPolicy.filter(message) == {:ok, except_message} diff --git a/test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs b/test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs index 8e1ad5bc8..f0432ea42 100644 --- a/test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicyTest do use Pleroma.DataCase @@ -17,14 +17,14 @@ test "pass filter if allow list is empty" do test "pass filter if allow list isn't empty and user in allow list" do actor = insert(:user) - Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => [actor.ap_id, "test-ap-id"]}) + clear_config([:mrf_user_allowlist], %{"localhost" => [actor.ap_id, "test-ap-id"]}) message = %{"actor" => actor.ap_id} assert UserAllowListPolicy.filter(message) == {:ok, message} end test "rejected if allow list isn't empty and user not in allow list" do actor = insert(:user) - Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => ["test-ap-id"]}) + clear_config([:mrf_user_allowlist], %{"localhost" => ["test-ap-id"]}) message = %{"actor" => actor.ap_id} assert {:reject, _} = UserAllowListPolicy.filter(message) end diff --git a/test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs b/test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs index 2bceb67ee..87d1d79b5 100644 --- a/test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do @@ -11,7 +11,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do setup do: clear_config([:mrf_vocabulary, :accept]) test "it accepts based on parent activity type" do - Pleroma.Config.put([:mrf_vocabulary, :accept], ["Like"]) + clear_config([:mrf_vocabulary, :accept], ["Like"]) message = %{ "type" => "Like", @@ -22,7 +22,7 @@ test "it accepts based on parent activity type" do end test "it accepts based on child object type" do - Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"]) + clear_config([:mrf_vocabulary, :accept], ["Create", "Note"]) message = %{ "type" => "Create", @@ -36,7 +36,7 @@ test "it accepts based on child object type" do end test "it does not accept disallowed child objects" do - Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"]) + clear_config([:mrf_vocabulary, :accept], ["Create", "Note"]) message = %{ "type" => "Create", @@ -50,7 +50,7 @@ test "it does not accept disallowed child objects" do end test "it does not accept disallowed parent types" do - Pleroma.Config.put([:mrf_vocabulary, :accept], ["Announce", "Note"]) + clear_config([:mrf_vocabulary, :accept], ["Announce", "Note"]) message = %{ "type" => "Create", @@ -68,7 +68,7 @@ test "it does not accept disallowed parent types" do setup do: clear_config([:mrf_vocabulary, :reject]) test "it rejects based on parent activity type" do - Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"]) + clear_config([:mrf_vocabulary, :reject], ["Like"]) message = %{ "type" => "Like", @@ -79,7 +79,7 @@ test "it rejects based on parent activity type" do end test "it rejects based on child object type" do - Pleroma.Config.put([:mrf_vocabulary, :reject], ["Note"]) + clear_config([:mrf_vocabulary, :reject], ["Note"]) message = %{ "type" => "Create", @@ -93,7 +93,7 @@ test "it rejects based on child object type" do end test "it passes through objects that aren't disallowed" do - Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"]) + clear_config([:mrf_vocabulary, :reject], ["Like"]) message = %{ "type" => "Announce", diff --git a/test/pleroma/web/activity_pub/mrf_test.exs b/test/pleroma/web/activity_pub/mrf_test.exs index 44a9cf086..61d308b97 100644 --- a/test/pleroma/web/activity_pub/mrf_test.exs +++ b/test/pleroma/web/activity_pub/mrf_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRFTest do @@ -68,7 +68,12 @@ test "it works as expected with noop policy" do clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoOpPolicy]) expected = %{ - mrf_policies: ["NoOpPolicy"], + mrf_policies: ["NoOpPolicy", "HashtagPolicy"], + mrf_hashtag: %{ + federated_timeline_removal: [], + reject: [], + sensitive: ["nsfw"] + }, exclusions: false } @@ -79,8 +84,13 @@ test "it works as expected with mock policy" do clear_config([:mrf, :policies], [MRFModuleMock]) expected = %{ - mrf_policies: ["MRFModuleMock"], + mrf_policies: ["MRFModuleMock", "HashtagPolicy"], mrf_module_mock: "some config data", + mrf_hashtag: %{ + federated_timeline_removal: [], + reject: [], + sensitive: ["nsfw"] + }, exclusions: false } diff --git a/test/pleroma/web/activity_pub/object_validators/accept_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/accept_validation_test.exs index d6111ba41..ddb302f6e 100644 --- a/test/pleroma/web/activity_pub/object_validators/accept_validation_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/accept_validation_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptValidationTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.ObjectValidator diff --git a/test/pleroma/web/activity_pub/object_validators/announce_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/announce_validation_test.exs index 4771c4698..939922127 100644 --- a/test/pleroma/web/activity_pub/object_validators/announce_validation_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/announce_validation_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidationTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Object alias Pleroma.Web.ActivityPub.Builder @@ -18,7 +18,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidationTest do announcer = insert(:user) {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - object = Object.normalize(post_activity, false) + object = Object.normalize(post_activity, fetch: false) {:ok, valid_announce, []} = Builder.announce(announcer, object) %{ @@ -81,7 +81,7 @@ test "returns an error if the actor can't announce the object", %{ {:ok, post_activity} = CommonAPI.post(user, %{status: "a secret post", visibility: "private"}) - object = Object.normalize(post_activity, false) + object = Object.normalize(post_activity, fetch: false) # Another user can't announce it {:ok, announce, []} = Builder.announce(announcer, object, public: false) diff --git a/test/pleroma/web/activity_pub/object_validators/article_note_validator_test.exs b/test/pleroma/web/activity_pub/object_validators/article_note_validator_test.exs index cc6dab872..e408c85c3 100644 --- a/test/pleroma/web/activity_pub/object_validators/article_note_validator_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/article_note_validator_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidatorTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator alias Pleroma.Web.ActivityPub.Utils diff --git a/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs b/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs index 2e1975a79..b775515e0 100644 --- a/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator diff --git a/test/pleroma/web/activity_pub/object_validators/block_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/block_validation_test.exs index c08d4b2e8..ad6190892 100644 --- a/test/pleroma/web/activity_pub/object_validators/block_validation_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/block_validation_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidationTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.ObjectValidator diff --git a/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs index d7e299224..320854187 100644 --- a/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do @@ -17,7 +17,7 @@ test "it is invalid if the object already exists" do user = insert(:user) recipient = insert(:user) {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "hey") - object = Object.normalize(activity, false) + object = Object.normalize(activity, fetch: false) {:ok, create_data, _} = Builder.create(user, object.data, [recipient.ap_id]) @@ -149,7 +149,7 @@ test "does not validate if the message has no content", %{ test "does not validate if the message is longer than the remote_limit", %{ valid_chat_message: valid_chat_message } do - Pleroma.Config.put([:instance, :remote_limit], 2) + clear_config([:instance, :remote_limit], 2) refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) end diff --git a/test/pleroma/web/activity_pub/object_validators/delete_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/delete_validation_test.exs index 02683b899..7ae4399ed 100644 --- a/test/pleroma/web/activity_pub/object_validators/delete_validation_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/delete_validation_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidationTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Object alias Pleroma.Web.ActivityPub.Builder diff --git a/test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs b/test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs index 582e6d785..7fd98266a 100644 --- a/test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactHandlingTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.ObjectValidator diff --git a/test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs index 6e1378be2..d2a9b72a5 100644 --- a/test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.FollowValidationTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.ObjectValidator diff --git a/test/pleroma/web/activity_pub/object_validators/like_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/like_validation_test.exs index 2c033b7e2..55f67232e 100644 --- a/test/pleroma/web/activity_pub/object_validators/like_validation_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/like_validation_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidationTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.ActivityPub.ObjectValidator alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator diff --git a/test/pleroma/web/activity_pub/object_validators/reject_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/reject_validation_test.exs index 370bb6e5c..562656a5f 100644 --- a/test/pleroma/web/activity_pub/object_validators/reject_validation_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/reject_validation_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.RejectValidationTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.ObjectValidator diff --git a/test/pleroma/web/activity_pub/object_validators/undo_handling_test.exs b/test/pleroma/web/activity_pub/object_validators/undo_handling_test.exs index 75bbcc4b6..505ca2d2f 100644 --- a/test/pleroma/web/activity_pub/object_validators/undo_handling_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/undo_handling_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoHandlingTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.ObjectValidator diff --git a/test/pleroma/web/activity_pub/object_validators/update_handling_test.exs b/test/pleroma/web/activity_pub/object_validators/update_handling_test.exs index 5e80cf731..15e4a82cd 100644 --- a/test/pleroma/web/activity_pub/object_validators/update_handling_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/update_handling_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.ObjectValidator diff --git a/test/pleroma/web/activity_pub/pipeline_test.exs b/test/pleroma/web/activity_pub/pipeline_test.exs index 210a06563..e606fa3d1 100644 --- a/test/pleroma/web/activity_pub/pipeline_test.exs +++ b/test/pleroma/web/activity_pub/pipeline_test.exs @@ -1,16 +1,34 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.PipelineTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true - import Mock + import Mox import Pleroma.Factory + alias Pleroma.ConfigMock + alias Pleroma.Web.ActivityPub.ActivityPubMock + alias Pleroma.Web.ActivityPub.MRFMock + alias Pleroma.Web.ActivityPub.ObjectValidatorMock + alias Pleroma.Web.ActivityPub.SideEffectsMock + alias Pleroma.Web.FederatorMock + + setup :verify_on_exit! + describe "common_pipeline/2" do setup do - clear_config([:instance, :federating], true) + ObjectValidatorMock + |> expect(:validate, fn o, m -> {:ok, o, m} end) + + MRFMock + |> expect(:pipeline_filter, fn o, m -> {:ok, o, m} end) + + SideEffectsMock + |> expect(:handle, fn o, m -> {:ok, o, m} end) + |> expect(:handle_after_transaction, fn m -> m end) + :ok end @@ -21,159 +39,65 @@ test "when given an `object_data` in meta, Federation will receive a the origina activity_with_object = %{activity | data: Map.put(activity.data, "object", object)} - with_mocks([ - {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, - { - Pleroma.Web.ActivityPub.MRF, - [], - [pipeline_filter: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.ActivityPub, - [], - [persist: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.SideEffects, - [], - [ - handle: fn o, m -> {:ok, o, m} end, - handle_after_transaction: fn m -> m end - ] - }, - { - Pleroma.Web.Federator, - [], - [publish: fn _o -> :ok end] - } - ]) do - assert {:ok, ^activity, ^meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) + ActivityPubMock + |> expect(:persist, fn _, m -> {:ok, activity, m} end) - assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) - refute called(Pleroma.Web.Federator.publish(activity)) - assert_called(Pleroma.Web.Federator.publish(activity_with_object)) - end + FederatorMock + |> expect(:publish, fn ^activity_with_object -> :ok end) + + ConfigMock + |> expect(:get, fn [:instance, :federating] -> true end) + + assert {:ok, ^activity, ^meta} = + Pleroma.Web.ActivityPub.Pipeline.common_pipeline( + activity.data, + meta + ) end test "it goes through validation, filtering, persisting, side effects and federation for local activities" do activity = insert(:note_activity) meta = [local: true] - with_mocks([ - {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, - { - Pleroma.Web.ActivityPub.MRF, - [], - [pipeline_filter: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.ActivityPub, - [], - [persist: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.SideEffects, - [], - [ - handle: fn o, m -> {:ok, o, m} end, - handle_after_transaction: fn m -> m end - ] - }, - { - Pleroma.Web.Federator, - [], - [publish: fn _o -> :ok end] - } - ]) do - assert {:ok, ^activity, ^meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) + ActivityPubMock + |> expect(:persist, fn _, m -> {:ok, activity, m} end) - assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) - assert_called(Pleroma.Web.Federator.publish(activity)) - end + FederatorMock + |> expect(:publish, fn ^activity -> :ok end) + + ConfigMock + |> expect(:get, fn [:instance, :federating] -> true end) + + assert {:ok, ^activity, ^meta} = + Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity.data, meta) end test "it goes through validation, filtering, persisting, side effects without federation for remote activities" do activity = insert(:note_activity) meta = [local: false] - with_mocks([ - {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, - { - Pleroma.Web.ActivityPub.MRF, - [], - [pipeline_filter: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.ActivityPub, - [], - [persist: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.SideEffects, - [], - [handle: fn o, m -> {:ok, o, m} end, handle_after_transaction: fn m -> m end] - }, - { - Pleroma.Web.Federator, - [], - [] - } - ]) do - assert {:ok, ^activity, ^meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) + ActivityPubMock + |> expect(:persist, fn _, m -> {:ok, activity, m} end) - assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) - end + ConfigMock + |> expect(:get, fn [:instance, :federating] -> true end) + + assert {:ok, ^activity, ^meta} = + Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity.data, meta) end test "it goes through validation, filtering, persisting, side effects without federation for local activities if federation is deactivated" do - clear_config([:instance, :federating], false) - activity = insert(:note_activity) meta = [local: true] - with_mocks([ - {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, - { - Pleroma.Web.ActivityPub.MRF, - [], - [pipeline_filter: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.ActivityPub, - [], - [persist: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.SideEffects, - [], - [handle: fn o, m -> {:ok, o, m} end, handle_after_transaction: fn m -> m end] - }, - { - Pleroma.Web.Federator, - [], - [] - } - ]) do - assert {:ok, ^activity, ^meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) + ActivityPubMock + |> expect(:persist, fn _, m -> {:ok, activity, m} end) - assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) - end + ConfigMock + |> expect(:get, fn [:instance, :federating] -> false end) + + assert {:ok, ^activity, ^meta} = + Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity.data, meta) end end end diff --git a/test/pleroma/web/activity_pub/publisher_test.exs b/test/pleroma/web/activity_pub/publisher_test.exs index 3503d25b2..f0ce3d7f2 100644 --- a/test/pleroma/web/activity_pub/publisher_test.exs +++ b/test/pleroma/web/activity_pub/publisher_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.PublisherTest do @@ -322,7 +322,7 @@ test "publish to url with with different ports" do actor = insert(:user) note_activity = insert(:note_activity, user: actor) - object = Object.normalize(note_activity) + object = Object.normalize(note_activity, fetch: false) activity_path = String.trim_leading(note_activity.data["id"], Pleroma.Web.Endpoint.url()) object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) diff --git a/test/pleroma/web/activity_pub/relay_test.exs b/test/pleroma/web/activity_pub/relay_test.exs index 3284980f7..2aa07d1b5 100644 --- a/test/pleroma/web/activity_pub/relay_test.exs +++ b/test/pleroma/web/activity_pub/relay_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.RelayTest do @@ -84,7 +84,7 @@ test "force unfollow when target service is dead" do ) Pleroma.Repo.delete(user) - Cachex.clear(:user_cache) + User.invalidate_cache(user) assert {:ok, %Activity{} = activity} = Relay.unfollow(user_ap_id, %{force: true}) diff --git a/test/pleroma/web/activity_pub/side_effects/delete_test.exs b/test/pleroma/web/activity_pub/side_effects/delete_test.exs new file mode 100644 index 000000000..20f0d4b70 --- /dev/null +++ b/test/pleroma/web/activity_pub/side_effects/delete_test.exs @@ -0,0 +1,147 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.SideEffects.DeleteTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase, async: true + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.SideEffects + alias Pleroma.Web.CommonAPI + + alias Pleroma.LoggerMock + alias Pleroma.Web.ActivityPub.ActivityPubMock + + import Mox + import Pleroma.Factory + + describe "user deletion" do + setup do + user = insert(:user) + + {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id) + {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true) + + %{ + user: user, + delete_user: delete_user + } + end + + test "it handles user deletions", %{delete_user: delete, user: user} do + {:ok, _delete, _} = SideEffects.handle(delete) + ObanHelpers.perform_all() + + refute User.get_cached_by_ap_id(user.ap_id).is_active + end + end + + describe "object deletion" do + setup do + user = insert(:user) + other_user = insert(:user) + + {:ok, op} = CommonAPI.post(other_user, %{status: "big oof"}) + {:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op}) + {:ok, favorite} = CommonAPI.favorite(user, post.id) + object = Object.normalize(post, fetch: false) + {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"]) + {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true) + + %{ + user: user, + delete: delete, + post: post, + object: object, + op: op, + favorite: favorite + } + end + + test "it handles object deletions", %{ + delete: delete, + post: post, + object: object, + user: user, + op: op, + favorite: favorite + } do + object_id = object.id + user_id = user.id + + ActivityPubMock + |> expect(:stream_out, fn ^delete -> nil end) + |> expect(:stream_out_participations, fn %Object{id: ^object_id}, %User{id: ^user_id} -> + nil + end) + + {:ok, _delete, _} = SideEffects.handle(delete) + user = User.get_cached_by_ap_id(object.data["actor"]) + + object = Object.get_by_id(object.id) + assert object.data["type"] == "Tombstone" + refute Activity.get_by_id(post.id) + refute Activity.get_by_id(favorite.id) + + user = User.get_by_id(user.id) + assert user.note_count == 0 + + object = Object.normalize(op.data["object"], fetch: false) + + assert object.data["repliesCount"] == 0 + end + + test "it handles object deletions when the object itself has been pruned", %{ + delete: delete, + post: post, + object: object, + user: user, + op: op + } do + object_id = object.id + user_id = user.id + + ActivityPubMock + |> expect(:stream_out, fn ^delete -> nil end) + |> expect(:stream_out_participations, fn %Object{id: ^object_id}, %User{id: ^user_id} -> + nil + end) + + {:ok, _delete, _} = SideEffects.handle(delete) + user = User.get_cached_by_ap_id(object.data["actor"]) + + object = Object.get_by_id(object.id) + assert object.data["type"] == "Tombstone" + refute Activity.get_by_id(post.id) + + user = User.get_by_id(user.id) + assert user.note_count == 0 + + object = Object.normalize(op.data["object"], fetch: false) + + assert object.data["repliesCount"] == 0 + end + + test "it logs issues with objects deletion", %{ + delete: delete, + object: object + } do + {:ok, _object} = + object + |> Object.change(%{data: Map.delete(object.data, "actor")}) + |> Repo.update() + + LoggerMock + |> expect(:error, fn str -> assert str =~ "The object doesn't have an actor" end) + + {:error, :no_object_actor} = SideEffects.handle(delete) + end + end +end diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs index 297fc0b84..13167f50a 100644 --- a/test/pleroma/web/activity_pub/side_effects_test.exs +++ b/test/pleroma/web/activity_pub/side_effects_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.SideEffectsTest do @@ -19,7 +19,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do alias Pleroma.Web.ActivityPub.SideEffects alias Pleroma.Web.CommonAPI - import ExUnit.CaptureLog import Mock import Pleroma.Factory @@ -131,115 +130,6 @@ test "it uses a given changeset to update", %{user: user, update: update} do end end - describe "delete objects" do - setup do - user = insert(:user) - other_user = insert(:user) - - {:ok, op} = CommonAPI.post(other_user, %{status: "big oof"}) - {:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op}) - {:ok, favorite} = CommonAPI.favorite(user, post.id) - object = Object.normalize(post) - {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"]) - {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id) - {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true) - {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true) - - %{ - user: user, - delete: delete, - post: post, - object: object, - delete_user: delete_user, - op: op, - favorite: favorite - } - end - - test "it handles object deletions", %{ - delete: delete, - post: post, - object: object, - user: user, - op: op, - favorite: favorite - } do - with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], - stream_out: fn _ -> nil end, - stream_out_participations: fn _, _ -> nil end do - {:ok, delete, _} = SideEffects.handle(delete) - user = User.get_cached_by_ap_id(object.data["actor"]) - - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete)) - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user)) - end - - object = Object.get_by_id(object.id) - assert object.data["type"] == "Tombstone" - refute Activity.get_by_id(post.id) - refute Activity.get_by_id(favorite.id) - - user = User.get_by_id(user.id) - assert user.note_count == 0 - - object = Object.normalize(op.data["object"], false) - - assert object.data["repliesCount"] == 0 - end - - test "it handles object deletions when the object itself has been pruned", %{ - delete: delete, - post: post, - object: object, - user: user, - op: op - } do - with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], - stream_out: fn _ -> nil end, - stream_out_participations: fn _, _ -> nil end do - {:ok, delete, _} = SideEffects.handle(delete) - user = User.get_cached_by_ap_id(object.data["actor"]) - - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete)) - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user)) - end - - object = Object.get_by_id(object.id) - assert object.data["type"] == "Tombstone" - refute Activity.get_by_id(post.id) - - user = User.get_by_id(user.id) - assert user.note_count == 0 - - object = Object.normalize(op.data["object"], false) - - assert object.data["repliesCount"] == 0 - end - - test "it handles user deletions", %{delete_user: delete, user: user} do - {:ok, _delete, _} = SideEffects.handle(delete) - ObanHelpers.perform_all() - - assert User.get_cached_by_ap_id(user.ap_id).deactivated - end - - test "it logs issues with objects deletion", %{ - delete: delete, - object: object - } do - {:ok, object} = - object - |> Object.change(%{data: Map.delete(object.data, "actor")}) - |> Repo.update() - - Object.invalid_object_cache(object) - - assert capture_log(fn -> - {:error, :no_object_actor} = SideEffects.handle(delete) - end) =~ "object doesn't have an actor" - end - end - describe "EmojiReact objects" do setup do poster = insert(:user) @@ -269,20 +159,12 @@ test "creates a notification", %{emoji_react: emoji_react, poster: poster} do describe "delete users with confirmation pending" do setup do - user = insert(:user, confirmation_pending: true) + user = insert(:user, is_confirmed: false) {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id) {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true) {:ok, delete: delete_user, user: user} end - test "when activation is not required", %{delete: delete, user: user} do - clear_config([:instance, :account_activation_required], false) - {:ok, _, _} = SideEffects.handle(delete) - ObanHelpers.perform_all() - - assert User.get_cached_by_id(user.id).deactivated - end - test "when activation is required", %{delete: delete, user: user} do clear_config([:instance, :account_activation_required], true) {:ok, _, _} = SideEffects.handle(delete) diff --git a/test/pleroma/web/activity_pub/transmogrifier/accept_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/accept_handling_test.exs index 485216487..58490076d 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/accept_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/accept_handling_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.AcceptHandlingTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.User alias Pleroma.Web.ActivityPub.Transmogrifier diff --git a/test/pleroma/web/activity_pub/transmogrifier/add_remove_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/add_remove_handling_test.exs new file mode 100644 index 000000000..fc7757125 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/add_remove_handling_test.exs @@ -0,0 +1,172 @@ +defmodule Pleroma.Web.ActivityPub.Transmogrifier.AddRemoveHandlingTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase, async: true + + require Pleroma.Constants + + import Pleroma.Factory + + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + + test "it accepts Add/Remove activities" do + user = + "test/fixtures/users_mock/user.json" + |> File.read!() + |> String.replace("{{nickname}}", "lain") + + object_id = "c61d6733-e256-4fe1-ab13-1e369789423f" + + object = + "test/fixtures/statuses/note.json" + |> File.read!() + |> String.replace("{{nickname}}", "lain") + |> String.replace("{{object_id}}", object_id) + + object_url = "https://example.com/objects/#{object_id}" + + actor = "https://example.com/users/lain" + + Tesla.Mock.mock(fn + %{ + method: :get, + url: ^actor + } -> + %Tesla.Env{ + status: 200, + body: user, + headers: [{"content-type", "application/activity+json"}] + } + + %{ + method: :get, + url: ^object_url + } -> + %Tesla.Env{ + status: 200, + body: object, + headers: [{"content-type", "application/activity+json"}] + } + + %{method: :get, url: "https://example.com/users/lain/collections/featured"} -> + %Tesla.Env{ + status: 200, + body: + "test/fixtures/users_mock/masto_featured.json" + |> File.read!() + |> String.replace("{{domain}}", "example.com") + |> String.replace("{{nickname}}", "lain"), + headers: [{"content-type", "application/activity+json"}] + } + end) + + message = %{ + "id" => "https://example.com/objects/d61d6733-e256-4fe1-ab13-1e369789423f", + "actor" => actor, + "object" => object_url, + "target" => "https://example.com/users/lain/collections/featured", + "type" => "Add", + "to" => [Pleroma.Constants.as_public()], + "cc" => ["https://example.com/users/lain/followers"] + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + assert activity.data == message + user = User.get_cached_by_ap_id(actor) + assert user.pinned_objects[object_url] + + remove = %{ + "id" => "http://localhost:400/objects/d61d6733-e256-4fe1-ab13-1e369789423d", + "actor" => actor, + "object" => object_url, + "target" => "https://example.com/users/lain/collections/featured", + "type" => "Remove", + "to" => [Pleroma.Constants.as_public()], + "cc" => ["https://example.com/users/lain/followers"] + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(remove) + assert activity.data == remove + + user = refresh_record(user) + refute user.pinned_objects[object_url] + end + + test "Add/Remove activities for remote users without featured address" do + user = insert(:user, local: false, domain: "example.com") + + user = + user + |> Ecto.Changeset.change(featured_address: nil) + |> Repo.update!() + + %{host: host} = URI.parse(user.ap_id) + + user_data = + "test/fixtures/users_mock/user.json" + |> File.read!() + |> String.replace("{{nickname}}", user.nickname) + + object_id = "c61d6733-e256-4fe1-ab13-1e369789423f" + + object = + "test/fixtures/statuses/note.json" + |> File.read!() + |> String.replace("{{nickname}}", user.nickname) + |> String.replace("{{object_id}}", object_id) + + object_url = "https://#{host}/objects/#{object_id}" + + actor = "https://#{host}/users/#{user.nickname}" + + featured = "https://#{host}/users/#{user.nickname}/collections/featured" + + Tesla.Mock.mock(fn + %{ + method: :get, + url: ^actor + } -> + %Tesla.Env{ + status: 200, + body: user_data, + headers: [{"content-type", "application/activity+json"}] + } + + %{ + method: :get, + url: ^object_url + } -> + %Tesla.Env{ + status: 200, + body: object, + headers: [{"content-type", "application/activity+json"}] + } + + %{method: :get, url: ^featured} -> + %Tesla.Env{ + status: 200, + body: + "test/fixtures/users_mock/masto_featured.json" + |> File.read!() + |> String.replace("{{domain}}", "#{host}") + |> String.replace("{{nickname}}", user.nickname), + headers: [{"content-type", "application/activity+json"}] + } + end) + + message = %{ + "id" => "https://#{host}/objects/d61d6733-e256-4fe1-ab13-1e369789423f", + "actor" => actor, + "object" => object_url, + "target" => "https://#{host}/users/#{user.nickname}/collections/featured", + "type" => "Add", + "to" => [Pleroma.Constants.as_public()], + "cc" => ["https://#{host}/users/#{user.nickname}/followers"] + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + assert activity.data == message + user = User.get_cached_by_ap_id(actor) + assert user.pinned_objects[object_url] + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs index c06bbc5e9..1886fea3f 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnnounceHandlingTest do @@ -130,7 +130,7 @@ test "it works for incoming announces with an inlined activity" do assert data["id"] == "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" - object = Object.normalize(data["object"]) + object = Object.normalize(data["object"], fetch: false) assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368" assert object.data["content"] == "this is a private toot" @@ -158,7 +158,7 @@ test "it does not clobber the addressing on announce activities" do data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!() - |> Map.put("object", Object.normalize(activity).data["id"]) + |> Map.put("object", Object.normalize(activity, fetch: false).data["id"]) |> Map.put("to", ["http://mastodon.example.org/users/admin/followers"]) |> Map.put("cc", []) diff --git a/test/pleroma/web/activity_pub/transmogrifier/answer_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/answer_handling_test.exs index a1c2ba28a..9c2ef547b 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/answer_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/answer_handling_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnswerHandlingTest do @@ -26,7 +26,7 @@ test "incoming, rewrites Note to Answer and increments vote counters" do poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert object.data["repliesCount"] == nil data = @@ -37,7 +37,7 @@ test "incoming, rewrites Note to Answer and increments vote counters" do |> Kernel.put_in(["object", "to"], user.ap_id) {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - answer_object = Object.normalize(activity) + answer_object = Object.normalize(activity, fetch: false) assert answer_object.data["type"] == "Answer" assert answer_object.data["inReplyTo"] == object.data["id"] @@ -62,7 +62,7 @@ test "outgoing, rewrites Answer to Note" do poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} }) - poll_object = Object.normalize(poll_activity) + poll_object = Object.normalize(poll_activity, fetch: false) # TODO: Replace with CommonAPI vote creation when implemented data = File.read!("test/fixtures/mastodon-vote.json") diff --git a/test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs index b0ae804c5..5dbc5eb95 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.ArticleHandlingTest do @@ -25,7 +25,7 @@ test "Pterotype (Wordpress Plugin) Article" do {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) + object = Object.normalize(data["object"], fetch: false) assert object.data["name"] == "The end is near: Mastodon plans to drop OStatus support" @@ -75,7 +75,7 @@ test "Prismo Article" do data = File.read!("test/fixtures/prismo-url-map.json") |> Jason.decode!() {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) + object = Object.normalize(data["object"], fetch: false) assert object.data["url"] == "https://prismo.news/posts/83" end diff --git a/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs index 7a2ac5d4d..e733f167d 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.AudioHandlingTest do @@ -35,7 +35,7 @@ test "it works for incoming listens" do {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert object.data["title"] == "lain radio episode 1" assert object.data["artist"] == "lain" @@ -57,7 +57,7 @@ test "Funkwhale Audio object" do {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - assert object = Object.normalize(activity, false) + assert object = Object.normalize(activity, fetch: false) assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] diff --git a/test/pleroma/web/activity_pub/transmogrifier/block_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/block_handling_test.exs index 679c33c6c..70da06d2e 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/block_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/block_handling_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.BlockHandlingTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Activity alias Pleroma.User diff --git a/test/pleroma/web/activity_pub/transmogrifier/chat_message_test.exs b/test/pleroma/web/activity_pub/transmogrifier/chat_message_test.exs index 2adaa1ade..958675835 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/chat_message_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/chat_message_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.ChatMessageTest do @@ -134,7 +134,7 @@ test "it doesn't work for deactivated users" do ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now(), - deactivated: true + is_active: false ) _recipient = insert(:user, ap_id: List.first(data["to"]), local: true) diff --git a/test/pleroma/web/activity_pub/transmogrifier/delete_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/delete_handling_test.exs index cffaa7c44..b7160bf58 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/delete_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/delete_handling_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.DeleteHandlingTest do @@ -40,7 +40,7 @@ test "it works for incoming deletes" do assert actor == deleting_user.ap_id # Objects are replaced by a tombstone object. - object = Object.normalize(activity.data["object"]) + object = Object.normalize(activity.data["object"], fetch: false) assert object.data["type"] == "Tombstone" end @@ -48,9 +48,10 @@ test "it works for incoming when the object has been pruned" do activity = insert(:note_activity) {:ok, object} = - Object.normalize(activity.data["object"]) + Object.normalize(activity.data["object"], fetch: false) |> Repo.delete() + # TODO: mock cachex Cachex.del(:object_cache, "object:#{object.data["id"]}") deleting_user = insert(:user) @@ -96,7 +97,7 @@ test "it works for incoming user deletes" do {:ok, _} = Transmogrifier.handle_incoming(data) ObanHelpers.perform_all() - assert User.get_cached_by_ap_id(ap_id).deactivated + refute User.get_cached_by_ap_id(ap_id).is_active end test "it fails for incoming user deletes with spoofed origin" do diff --git a/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs index aea4ed6f8..20424ee82 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiReactHandlingTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Activity alias Pleroma.Object diff --git a/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs index d7c55cfbe..c4879fda1 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.EventHandlingTest do diff --git a/test/pleroma/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/follow_handling_test.exs index 985c26def..604444a4c 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/follow_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/follow_handling_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do @@ -133,7 +133,7 @@ test "it works for follow requests when you are already followed, creating a new end test "it rejects incoming follow requests from blocked users when deny_follow_blocked is enabled" do - Pleroma.Config.put([:user, :deny_follow_blocked], true) + clear_config([:user, :deny_follow_blocked], true) user = insert(:user) {:ok, target} = User.get_or_fetch("http://mastodon.example.org/users/admin") diff --git a/test/pleroma/web/activity_pub/transmogrifier/like_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/like_handling_test.exs index 967bad151..57d74349a 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/like_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/like_handling_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.LikeHandlingTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Activity alias Pleroma.Web.ActivityPub.Transmogrifier diff --git a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs index b4a006aec..deb956410 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do @@ -28,7 +28,7 @@ test "it works for incoming notices with tag not being an array (kroeg)" do data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Jason.decode!() {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) + object = Object.normalize(data["object"], fetch: false) assert object.data["emoji"] == %{ "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png" @@ -37,9 +37,10 @@ test "it works for incoming notices with tag not being an array (kroeg)" do data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Jason.decode!() {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) + object = Object.normalize(data["object"], fetch: false) - assert "test" in object.data["tag"] + assert "test" in Object.tags(object) + assert Object.hashtags(object) == ["test"] end test "it cleans up incoming notices which are not really DMs" do @@ -66,7 +67,7 @@ test "it cleans up incoming notices which are not really DMs" do assert data["to"] == [] assert data["cc"] == to - object_data = Object.normalize(activity).data + object_data = Object.normalize(activity, fetch: false).data assert object_data["to"] == [] assert object_data["cc"] == to @@ -78,7 +79,7 @@ test "it ignores an incoming notice if we already have it" do data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!() - |> Map.put("object", Object.normalize(activity).data) + |> Map.put("object", Object.normalize(activity, fetch: false).data) {:ok, returned_activity} = Transmogrifier.handle_incoming(data) @@ -97,7 +98,7 @@ test "it fetches reply-to activities if we don't have them" do data = Map.put(data, "object", object) {:ok, returned_activity} = Transmogrifier.handle_incoming(data) - returned_object = Object.normalize(returned_activity, false) + returned_object = Object.normalize(returned_activity, fetch: false) assert %Activity{} = Activity.get_create_by_object_ap_id( @@ -123,7 +124,7 @@ test "it does not fetch reply-to activities beyond max replies depth limit" do allowed_thread_distance?: fn _ -> false end do {:ok, returned_activity} = Transmogrifier.handle_incoming(data) - returned_object = Object.normalize(returned_activity, false) + returned_object = Object.normalize(returned_activity, fetch: false) refute Activity.get_create_by_object_ap_id( "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" @@ -154,7 +155,7 @@ test "it does not crash if the object in inReplyTo can't be fetched" do test "it does not work for deactivated users" do data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!() - insert(:user, ap_id: data["actor"], deactivated: true) + insert(:user, ap_id: data["actor"], is_active: false) assert {:error, _} = Transmogrifier.handle_incoming(data) end @@ -179,7 +180,7 @@ test "it works for incoming notices" do assert data["actor"] == "http://mastodon.example.org/users/admin" - object_data = Object.normalize(data["object"]).data + object_data = Object.normalize(data["object"], fetch: false).data assert object_data["id"] == "http://mastodon.example.org/users/admin/statuses/99512778738411822" @@ -209,7 +210,7 @@ test "it works for incoming notices without the sensitive property but an nsfw h {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object_data = Object.normalize(data["object"], false).data + object_data = Object.normalize(data["object"], fetch: false).data assert object_data["sensitive"] == true end @@ -218,16 +219,17 @@ test "it works for incoming notices with hashtags" do data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Jason.decode!() {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) + object = Object.normalize(data["object"], fetch: false) - assert Enum.at(object.data["tag"], 2) == "moo" + assert Enum.at(Object.tags(object), 2) == "moo" + assert Object.hashtags(object) == ["moo"] end test "it works for incoming notices with contentMap" do data = File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Jason.decode!() {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) + object = Object.normalize(data["object"], fetch: false) assert object.data["content"] == "

@lain

" @@ -237,7 +239,7 @@ test "it works for incoming notices with to/cc not being an array (kroeg)" do data = File.read!("test/fixtures/kroeg-post-activity.json") |> Jason.decode!() {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) + object = Object.normalize(data["object"], fetch: false) assert object.data["content"] == "

henlo from my Psion netBook

message sent from my Psion netBook

" @@ -415,7 +417,7 @@ test "schedules background fetching of `replies` items if max thread depth limit data: data, items: items } do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 10) + clear_config([:instance, :federation_incoming_replies_max_depth], 10) {:ok, _activity} = Transmogrifier.handle_incoming(data) @@ -427,7 +429,7 @@ test "schedules background fetching of `replies` items if max thread depth limit test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows", %{data: data} do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) + clear_config([:instance, :federation_incoming_replies_max_depth], 0) {:ok, _activity} = Transmogrifier.handle_incoming(data) @@ -464,7 +466,7 @@ test "schedules background fetching of `replies` items if max thread depth limit federation_output: federation_output, replies_uris: replies_uris } do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 1) + clear_config([:instance, :federation_incoming_replies_max_depth], 1) {:ok, _activity} = Transmogrifier.handle_incoming(federation_output) @@ -476,7 +478,7 @@ test "schedules background fetching of `replies` items if max thread depth limit test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows", %{federation_output: federation_output} do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) + clear_config([:instance, :federation_incoming_replies_max_depth], 0) {:ok, _activity} = Transmogrifier.handle_incoming(federation_output) @@ -551,7 +553,7 @@ test "returns not modified object when hasn't containts inReplyTo field", %{data end test "returns object with inReplyTo when denied incoming reply", %{data: data} do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) + clear_config([:instance, :federation_incoming_replies_max_depth], 0) object_with_reply = Map.put(data["object"], "inReplyTo", "https://shitposter.club/notice/2827873") @@ -585,7 +587,7 @@ test "returns modified object when allowed incoming reply", %{data: data} do "https://mstdn.io/users/mayuutann/statuses/99568293732299394" ) - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 5) + clear_config([:instance, :federation_incoming_replies_max_depth], 5) modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) assert modified_object["inReplyTo"] == @@ -725,7 +727,7 @@ test "sets `replies` collection with a limited number of self-replies" do in_reply_to_status_id: id1 }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) replies_uris = Enum.map([self_reply1, self_reply2], fn a -> a.object.data["id"] end) assert %{"type" => "Collection", "items" => ^replies_uris} = diff --git a/test/pleroma/web/activity_pub/transmogrifier/question_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/question_handling_test.exs index 47f92cf4d..32cf51e59 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/question_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/question_handling_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.QuestionHandlingTest do @@ -22,7 +22,7 @@ test "Mastodon Question activity" do {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - object = Object.normalize(activity, false) + object = Object.normalize(activity, fetch: false) assert object.data["url"] == "https://mastodon.sdf.org/@rinpatch/102070944809637304" @@ -65,7 +65,7 @@ test "Mastodon Question activity" do {:ok, reply_activity} = CommonAPI.post(user, %{status: "hewwo", in_reply_to_id: activity.id}) - reply_object = Object.normalize(reply_activity, false) + reply_object = Object.normalize(reply_activity, fetch: false) assert reply_object.data["context"] == object.data["context"] assert reply_object.data["context_id"] == object.data["context_id"] @@ -101,7 +101,7 @@ test "Mastodon Question activity with HTML tags in plaintext" do |> Kernel.put_in(["object", "oneOf"], options) {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - object = Object.normalize(activity, false) + object = Object.normalize(activity, fetch: false) assert Enum.sort(object.data["oneOf"]) == Enum.sort(options) end @@ -147,7 +147,7 @@ test "Mastodon Question activity with custom emojis" do |> Kernel.put_in(["object", "tag"], tag) {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - object = Object.normalize(activity, false) + object = Object.normalize(activity, fetch: false) assert object.data["oneOf"] == options diff --git a/test/pleroma/web/activity_pub/transmogrifier/reject_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/reject_handling_test.exs index 5a3bef792..355e664d4 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/reject_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/reject_handling_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.RejectHandlingTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Activity alias Pleroma.User diff --git a/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs index fcfc7b4b6..f6e40722c 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.UndoHandlingTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Activity alias Pleroma.Object diff --git a/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs index c62d5e139..b1a064772 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.UserUpdateHandlingTest do @@ -103,7 +103,7 @@ test "it works with custom profile fields" do %{"name" => "foo1", "value" => "updated"} ] - Pleroma.Config.put([:instance, :max_remote_account_fields], 2) + clear_config([:instance, :max_remote_account_fields], 2) update_data = update_data diff --git a/test/pleroma/web/activity_pub/transmogrifier/video_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/video_handling_test.exs index 57411fafa..6ddf7c172 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/video_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/video_handling_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.Transmogrifier.VideoHandlingTest do @@ -24,7 +24,7 @@ test "skip converting the content when it is nil" do {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - assert object = Object.normalize(activity, false) + assert object = Object.normalize(activity, fetch: false) assert object.data["content"] == nil end @@ -34,7 +34,7 @@ test "it converts content of object to html" do {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - assert object = Object.normalize(activity, false) + assert object = Object.normalize(activity, fetch: false) assert object.data["content"] == "

Après avoir mené avec un certain succès la campagne « Dégooglisons Internet » en 2014, l’association Framasoft annonce fin 2019 arrêter progressivement un certain nombre de ses services alternatifs aux GAFAM. Pourquoi ?

Transcription par @aprilorg ici : https://www.april.org/deframasoftisons-internet-pierre-yves-gosset-framasoft

" @@ -70,7 +70,7 @@ test "it remaps video URLs as attachments if necessary" do {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - assert object = Object.normalize(activity, false) + assert object = Object.normalize(activity, fetch: false) assert object.data["attachment"] == [ %{ @@ -92,4 +92,34 @@ test "it remaps video URLs as attachments if necessary" do assert object.data["url"] == "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206" end + + test "it works for peertube videos with only their mpegURL map" do + data = + File.read!("test/fixtures/peertube/video-object-mpegURL-only.json") + |> Jason.decode!() + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert object = Object.normalize(activity, fetch: false) + + assert object.data["attachment"] == [ + %{ + "type" => "Link", + "mediaType" => "video/mp4", + "name" => nil, + "blurhash" => nil, + "url" => [ + %{ + "href" => + "https://peertube.stream/static/streaming-playlists/hls/abece3c3-b9c6-47f4-8040-f3eed8c602e6/abece3c3-b9c6-47f4-8040-f3eed8c602e6-1080-fragmented.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ] + + assert object.data["url"] == + "https://peertube.stream/videos/watch/abece3c3-b9c6-47f4-8040-f3eed8c602e6" + end end diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index 66ea7664a..4c3fcb44a 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do @@ -56,7 +56,7 @@ test "it accepts Flag activities" do other_user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) note_obj = %{ "type" => "Note", @@ -153,15 +153,6 @@ test "it turns mentions into tags" do end end - test "it adds the sensitive property" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"}) - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["object"]["sensitive"] - end - test "it adds the json-ld context and the conversation property" do user = insert(:user) @@ -202,7 +193,20 @@ test "it strips internal hashtag data" do test "it strips internal fields" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "#2hu :firefox:"}) + {:ok, activity} = + CommonAPI.post(user, %{ + status: "#2hu :firefox:", + generator: %{type: "Application", name: "TestClient", url: "https://pleroma.social"} + }) + + # Ensure injected application data made it into the activity + # as we don't have a Token to derive it from, otherwise it will + # be nil and the test will pass + assert %{ + type: "Application", + name: "TestClient", + url: "https://pleroma.social" + } == activity.object.data["generator"] {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) @@ -213,6 +217,7 @@ test "it strips internal fields" do assert is_nil(modified["object"]["announcements"]) assert is_nil(modified["object"]["announcement_count"]) assert is_nil(modified["object"]["context_id"]) + assert is_nil(modified["object"]["generator"]) end test "it strips internal fields of article" do @@ -281,6 +286,21 @@ test "it can handle Listen activities" do {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data) end + + test "custom emoji urls are URI encoded" do + # :dinosaur: filename has a space -> dino walking.gif + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "everybody do the dinosaur :dinosaur:"}) + + {:ok, prepared} = Transmogrifier.prepare_outgoing(activity.data) + + assert length(prepared["object"]["tag"]) == 1 + + url = prepared["object"]["tag"] |> List.first() |> Map.get("icon") |> Map.get("url") + + assert url == "http://localhost:4001/emoji/dino%20walking.gif" + end end describe "user upgrade" do diff --git a/test/pleroma/web/activity_pub/utils_test.exs b/test/pleroma/web/activity_pub/utils_test.exs index be9cd7d13..ee3e1014e 100644 --- a/test/pleroma/web/activity_pub/utils_test.exs +++ b/test/pleroma/web/activity_pub/utils_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.UtilsTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Activity alias Pleroma.Object alias Pleroma.Repo @@ -165,7 +165,7 @@ test "fetches existing votes" do } }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1]) assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes) end @@ -183,7 +183,7 @@ test "fetches only Create activities" do } }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0]) {:ok, _activity} = CommonAPI.favorite(user, activity.id) [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object) @@ -242,7 +242,7 @@ test "updates the state of the given follow activity" do test "updates likes" do user = insert(:user) activity = insert(:note_activity) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert {:ok, updated_object} = Utils.update_element_in_object( @@ -302,7 +302,7 @@ test "removes ap_id from likes" do describe "get_existing_like/2" do test "fetches existing like" do note_activity = insert(:note_activity) - assert object = Object.normalize(note_activity) + assert object = Object.normalize(note_activity, fetch: false) user = insert(:user) refute Utils.get_existing_like(user.ap_id, object) @@ -320,7 +320,7 @@ test "returns nil if announce not found" do test "fetches existing announce" do note_activity = insert(:note_activity) - assert object = Object.normalize(note_activity) + assert object = Object.normalize(note_activity, fetch: false) actor = insert(:user) {:ok, announce} = CommonAPI.repeat(note_activity.id, actor) @@ -412,7 +412,7 @@ test "returns false" do describe "lazy_put_activity_defaults/2" do test "returns map with id and published data" do note_activity = insert(:note_activity) - object = Object.normalize(note_activity) + object = Object.normalize(note_activity, fetch: false) res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]}) assert res["context"] == object.data["id"] assert res["context_id"] == object.id @@ -431,7 +431,7 @@ test "returns map with fake id and published data" do test "returns activity data with object" do note_activity = insert(:note_activity) - object = Object.normalize(note_activity) + object = Object.normalize(note_activity, fetch: false) res = Utils.lazy_put_activity_defaults(%{ diff --git a/test/pleroma/web/activity_pub/views/object_view_test.exs b/test/pleroma/web/activity_pub/views/object_view_test.exs index f0389845d..923515dec 100644 --- a/test/pleroma/web/activity_pub/views/object_view_test.exs +++ b/test/pleroma/web/activity_pub/views/object_view_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.ObjectViewTest do @@ -24,7 +24,7 @@ test "renders a note object" do test "renders a note activity" do note = insert(:note_activity) - object = Object.normalize(note) + object = Object.normalize(note, fetch: false) result = ObjectView.render("object.json", %{object: note}) @@ -56,7 +56,7 @@ test "renders `replies` collection for a note activity" do test "renders a like activity" do note = insert(:note_activity) - object = Object.normalize(note) + object = Object.normalize(note, fetch: false) user = insert(:user) {:ok, like_activity} = CommonAPI.favorite(user, note.id) @@ -70,7 +70,7 @@ test "renders a like activity" do test "renders an announce activity" do note = insert(:note_activity) - object = Object.normalize(note) + object = Object.normalize(note, fetch: false) user = insert(:user) {:ok, announce_activity} = CommonAPI.repeat(note.id, user) diff --git a/test/pleroma/web/activity_pub/views/user_view_test.exs b/test/pleroma/web/activity_pub/views/user_view_test.exs index 98c7c9d09..f2de4d332 100644 --- a/test/pleroma/web/activity_pub/views/user_view_test.exs +++ b/test/pleroma/web/activity_pub/views/user_view_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.UserViewTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.User @@ -80,6 +80,12 @@ test "renders an invisible user with the invisible property set to true" do assert %{"invisible" => true} = UserView.render("service.json", %{user: user}) end + test "renders AKAs" do + akas = ["https://i.tusooa.xyz/users/test-pleroma"] + user = insert(:user, also_known_as: akas) + assert %{"alsoKnownAs" => ^akas} = UserView.render("user.json", %{user: user}) + end + describe "endpoints" do test "local users have a usable endpoints structure" do user = insert(:user) diff --git a/test/pleroma/web/activity_pub/visibility_test.exs b/test/pleroma/web/activity_pub/visibility_test.exs index 836d44994..23485225d 100644 --- a/test/pleroma/web/activity_pub/visibility_test.exs +++ b/test/pleroma/web/activity_pub/visibility_test.exs @@ -1,11 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.VisibilityTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Activity + alias Pleroma.Object alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.CommonAPI import Pleroma.Factory @@ -107,7 +108,7 @@ test "is_list?", %{ assert Visibility.is_list?(list) end - test "visible_for_user?", %{ + test "visible_for_user? Activity", %{ public: public, private: private, direct: direct, @@ -149,17 +150,83 @@ test "visible_for_user?", %{ refute Visibility.visible_for_user?(private, unrelated) refute Visibility.visible_for_user?(direct, unrelated) + # Public and unlisted visible for unauthenticated + + assert Visibility.visible_for_user?(public, nil) + assert Visibility.visible_for_user?(unlisted, nil) + refute Visibility.visible_for_user?(private, nil) + refute Visibility.visible_for_user?(direct, nil) + # Visible for a list member assert Visibility.visible_for_user?(list, unrelated) end + test "visible_for_user? Object", %{ + public: public, + private: private, + direct: direct, + unlisted: unlisted, + user: user, + mentioned: mentioned, + following: following, + unrelated: unrelated, + list: list + } do + public = Object.normalize(public) + private = Object.normalize(private) + unlisted = Object.normalize(unlisted) + direct = Object.normalize(direct) + list = Object.normalize(list) + + # All visible to author + + assert Visibility.visible_for_user?(public, user) + assert Visibility.visible_for_user?(private, user) + assert Visibility.visible_for_user?(unlisted, user) + assert Visibility.visible_for_user?(direct, user) + assert Visibility.visible_for_user?(list, user) + + # All visible to a mentioned user + + assert Visibility.visible_for_user?(public, mentioned) + assert Visibility.visible_for_user?(private, mentioned) + assert Visibility.visible_for_user?(unlisted, mentioned) + assert Visibility.visible_for_user?(direct, mentioned) + assert Visibility.visible_for_user?(list, mentioned) + + # DM not visible for just follower + + assert Visibility.visible_for_user?(public, following) + assert Visibility.visible_for_user?(private, following) + assert Visibility.visible_for_user?(unlisted, following) + refute Visibility.visible_for_user?(direct, following) + refute Visibility.visible_for_user?(list, following) + + # Public and unlisted visible for unrelated user + + assert Visibility.visible_for_user?(public, unrelated) + assert Visibility.visible_for_user?(unlisted, unrelated) + refute Visibility.visible_for_user?(private, unrelated) + refute Visibility.visible_for_user?(direct, unrelated) + + # Public and unlisted visible for unauthenticated + + assert Visibility.visible_for_user?(public, nil) + assert Visibility.visible_for_user?(unlisted, nil) + refute Visibility.visible_for_user?(private, nil) + refute Visibility.visible_for_user?(direct, nil) + + # Visible for a list member + # assert Visibility.visible_for_user?(list, unrelated) + end + test "doesn't die when the user doesn't exist", %{ direct: direct, user: user } do Repo.delete(user) - Cachex.clear(:user_cache) + Pleroma.User.invalidate_cache(user) refute Visibility.is_private?(direct) end diff --git a/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs index e50d1425b..8cd9f939b 100644 --- a/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do @@ -46,104 +46,47 @@ test "with valid `admin_token` query parameter, skips OAuth scopes check" do assert json_response(conn, 200) end - describe "with [:auth, :enforce_oauth_admin_scope_usage]," do - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true) + test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope", + %{admin: admin} do + user = insert(:user) + url = "/api/pleroma/admin/users/#{user.nickname}" - test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope", - %{admin: admin} do - user = insert(:user) - url = "/api/pleroma/admin/users/#{user.nickname}" + good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"]) + good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"]) + good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"]) - good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"]) - good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"]) - good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"]) + bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"]) + bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"]) + bad_token3 = nil - bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"]) - bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"]) - bad_token3 = nil + for good_token <- [good_token1, good_token2, good_token3] do + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, good_token) + |> get(url) - for good_token <- [good_token1, good_token2, good_token3] do - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, good_token) - |> get(url) - - assert json_response(conn, 200) - end - - for good_token <- [good_token1, good_token2, good_token3] do - conn = - build_conn() - |> assign(:user, nil) - |> assign(:token, good_token) - |> get(url) - - assert json_response(conn, :forbidden) - end - - for bad_token <- [bad_token1, bad_token2, bad_token3] do - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, bad_token) - |> get(url) - - assert json_response(conn, :forbidden) - end + assert json_response(conn, 200) end - end - describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) + for good_token <- [good_token1, good_token2, good_token3] do + conn = + build_conn() + |> assign(:user, nil) + |> assign(:token, good_token) + |> get(url) - test "GET /api/pleroma/admin/users/:nickname requires " <> - "read:accounts or admin:read:accounts or broader scope", - %{admin: admin} do - user = insert(:user) - url = "/api/pleroma/admin/users/#{user.nickname}" + assert json_response(conn, :forbidden) + end - good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"]) - good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"]) - good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"]) - good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"]) - good_token5 = insert(:oauth_token, user: admin, scopes: ["read"]) + for bad_token <- [bad_token1, bad_token2, bad_token3] do + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, bad_token) + |> get(url) - good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5] - - bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"]) - bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"]) - bad_token3 = nil - - for good_token <- good_tokens do - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, good_token) - |> get(url) - - assert json_response(conn, 200) - end - - for good_token <- good_tokens do - conn = - build_conn() - |> assign(:user, nil) - |> assign(:token, good_token) - |> get(url) - - assert json_response(conn, :forbidden) - end - - for bad_token <- [bad_token1, bad_token2, bad_token3] do - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, bad_token) - |> get(url) - - assert json_response(conn, :forbidden) - end + assert json_response(conn, :forbidden) end end @@ -405,13 +348,9 @@ test "need_reboot flag", %{conn: conn} do setup do user = insert(:user) - date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!() - date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!() - date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!() - - insert(:note_activity, user: user, published: date1) - insert(:note_activity, user: user, published: date2) - insert(:note_activity, user: user, published: date3) + insert(:note_activity, user: user) + insert(:note_activity, user: user) + insert(:note_activity, user: user) %{user: user} end @@ -419,13 +358,22 @@ test "need_reboot flag", %{conn: conn} do test "renders user's statuses", %{conn: conn, user: user} do conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses") - assert json_response(conn, 200) |> length() == 3 + assert %{"total" => 3, "activities" => activities} = json_response(conn, 200) + assert length(activities) == 3 end - test "renders user's statuses with a limit", %{conn: conn, user: user} do - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2") + test "renders user's statuses with pagination", %{conn: conn, user: user} do + %{"total" => 3, "activities" => [activity1]} = + conn + |> get("/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=1&page=1") + |> json_response(200) - assert json_response(conn, 200) |> length() == 2 + %{"total" => 3, "activities" => [activity2]} = + conn + |> get("/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=1&page=2") + |> json_response(200) + + refute activity1 == activity2 end test "doesn't return private statuses by default", %{conn: conn, user: user} do @@ -433,9 +381,12 @@ test "doesn't return private statuses by default", %{conn: conn, user: user} do {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses") + %{"total" => 4, "activities" => activities} = + conn + |> get("/api/pleroma/admin/users/#{user.nickname}/statuses") + |> json_response(200) - assert json_response(conn, 200) |> length() == 4 + assert length(activities) == 4 end test "returns private statuses with godmode on", %{conn: conn, user: user} do @@ -443,9 +394,12 @@ test "returns private statuses with godmode on", %{conn: conn, user: user} do {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true") + %{"total" => 5, "activities" => activities} = + conn + |> get("/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true") + |> json_response(200) - assert json_response(conn, 200) |> length() == 5 + assert length(activities) == 5 end test "excludes reblogs by default", %{conn: conn, user: user} do @@ -453,13 +407,17 @@ test "excludes reblogs by default", %{conn: conn, user: user} do {:ok, activity} = CommonAPI.post(user, %{status: "."}) {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user) - conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses") - assert json_response(conn_res, 200) |> length() == 0 + assert %{"total" => 0, "activities" => []} == + conn + |> get("/api/pleroma/admin/users/#{other_user.nickname}/statuses") + |> json_response(200) - conn_res = - get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true") - - assert json_response(conn_res, 200) |> length() == 1 + assert %{"total" => 1, "activities" => [_]} = + conn + |> get( + "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true" + ) + |> json_response(200) end end @@ -849,42 +807,39 @@ test "GET /instances/:instance/statuses", %{conn: conn} do insert_pair(:note_activity, user: user) activity = insert(:note_activity, user: user2) - ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses") + %{"total" => 2, "activities" => activities} = + conn |> get("/api/pleroma/admin/instances/archae.me/statuses") |> json_response(200) - response = json_response(ret_conn, 200) + assert length(activities) == 2 - assert length(response) == 2 + %{"total" => 1, "activities" => [_]} = + conn |> get("/api/pleroma/admin/instances/test.com/statuses") |> json_response(200) - ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses") - - response = json_response(ret_conn, 200) - - assert length(response) == 1 - - ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses") - - response = json_response(ret_conn, 200) - - assert Enum.empty?(response) + %{"total" => 0, "activities" => []} = + conn |> get("/api/pleroma/admin/instances/nonexistent.com/statuses") |> json_response(200) CommonAPI.repeat(activity.id, user) - ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses") - response = json_response(ret_conn, 200) - assert length(response) == 2 + %{"total" => 2, "activities" => activities} = + conn |> get("/api/pleroma/admin/instances/archae.me/statuses") |> json_response(200) - ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true") - response = json_response(ret_conn, 200) - assert length(response) == 3 + assert length(activities) == 2 + + %{"total" => 3, "activities" => activities} = + conn + |> get("/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true") + |> json_response(200) + + assert length(activities) == 3 end end describe "PATCH /confirm_email" do test "it confirms emails of two users", %{conn: conn, admin: admin} do - [first_user, second_user] = insert_pair(:user, confirmation_pending: true) + [first_user, second_user] = insert_pair(:user, is_confirmed: false) - assert first_user.confirmation_pending == true - assert second_user.confirmation_pending == true + refute first_user.is_confirmed + refute second_user.is_confirmed ret_conn = patch(conn, "/api/pleroma/admin/users/confirm_email", %{ @@ -896,8 +851,11 @@ test "it confirms emails of two users", %{conn: conn, admin: admin} do assert ret_conn.status == 200 - assert first_user.confirmation_pending == true - assert second_user.confirmation_pending == true + first_user = User.get_by_id(first_user.id) + second_user = User.get_by_id(second_user.id) + + assert first_user.is_confirmed + assert second_user.is_confirmed log_entry = Repo.one(ModerationLog) @@ -910,7 +868,7 @@ test "it confirms emails of two users", %{conn: conn, admin: admin} do describe "PATCH /resend_confirmation_email" do test "it resend emails for two users", %{conn: conn, admin: admin} do - [first_user, second_user] = insert_pair(:user, confirmation_pending: true) + [first_user, second_user] = insert_pair(:user, is_confirmed: false) ret_conn = patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{ diff --git a/test/pleroma/web/admin_api/controllers/chat_controller_test.exs b/test/pleroma/web/admin_api/controllers/chat_controller_test.exs index 5aefa1e60..0e8f7beef 100644 --- a/test/pleroma/web/admin_api/controllers/chat_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/chat_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ChatControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true import Pleroma.Factory @@ -36,7 +36,7 @@ test "it deletes a message from the chat", %{conn: conn, admin: admin} do {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Hello darkness my old friend") - object = Object.normalize(message, false) + object = Object.normalize(message, fetch: false) chat = Chat.get(user.id, recipient.ap_id) recipient_chat = Chat.get(recipient.id, user.ap_id) @@ -143,7 +143,7 @@ test "it returns a chat", %{conn: conn} do recipient = insert(:user) {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Yo") - object = Object.normalize(message, false) + object = Object.normalize(message, fetch: false) chat = Chat.get(user.id, recipient.ap_id) cm_ref = MessageReference.for_chat_and_object(chat, object) @@ -183,7 +183,7 @@ test "GET /api/pleroma/admin/chats/:id", %{conn: conn, chat: chat} do recipient = insert(:user) {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Yo") - object = Object.normalize(message, false) + object = Object.normalize(message, fetch: false) chat = Chat.get(user.id, recipient.ap_id) cm_ref = MessageReference.for_chat_and_object(chat, object) diff --git a/test/pleroma/web/admin_api/controllers/config_controller_test.exs b/test/pleroma/web/admin_api/controllers/config_controller_test.exs index e6b203e74..c39c1b1e1 100644 --- a/test/pleroma/web/admin_api/controllers/config_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/config_controller_test.exs @@ -1,14 +1,13 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase import ExUnit.CaptureLog import Pleroma.Factory - alias Pleroma.Config alias Pleroma.ConfigDB setup do @@ -27,12 +26,12 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do setup do: clear_config(:configurable_from_database, true) test "when configuration from database is off", %{conn: conn} do - Config.put(:configurable_from_database, false) + clear_config(:configurable_from_database, false) conn = get(conn, "/api/pleroma/admin/config") assert json_response_and_validate_schema(conn, 400) == %{ - "error" => "To use this endpoint you need to enable configuration from database." + "error" => "You must enable configurable_from_database in your config file." } end @@ -171,7 +170,7 @@ test "POST /api/pleroma/admin/config with configdb disabled", %{conn: conn} do |> post("/api/pleroma/admin/config", %{"configs" => []}) assert json_response_and_validate_schema(conn, 400) == - %{"error" => "To use this endpoint you need to enable configuration from database."} + %{"error" => "You must enable configurable_from_database in your config file."} end describe "POST /api/pleroma/admin/config" do @@ -410,8 +409,7 @@ test "saving config with partial update", %{conn: conn} do end test "saving config which need pleroma reboot", %{conn: conn} do - chat = Config.get(:chat) - on_exit(fn -> Config.put(:chat, chat) end) + clear_config([:chat, :enabled], true) assert conn |> put_req_header("content-type", "application/json") @@ -456,8 +454,7 @@ test "saving config which need pleroma reboot", %{conn: conn} do end test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do - chat = Config.get(:chat) - on_exit(fn -> Config.put(:chat, chat) end) + clear_config([:chat, :enabled], true) assert conn |> put_req_header("content-type", "application/json") @@ -1413,6 +1410,82 @@ test "enables the welcome messages", %{conn: conn} do "need_reboot" => false } end + + test "custom instance thumbnail", %{conn: conn} do + clear_config([:instance]) + + params = %{ + "group" => ":pleroma", + "key" => ":instance", + "value" => [ + %{ + "tuple" => [ + ":instance_thumbnail", + "https://example.com/media/new_thumbnail.jpg" + ] + } + ] + } + + res = + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{"configs" => [params]}) + |> json_response_and_validate_schema(200) + + assert res == %{ + "configs" => [ + %{ + "db" => [":instance_thumbnail"], + "group" => ":pleroma", + "key" => ":instance", + "value" => params["value"] + } + ], + "need_reboot" => false + } + + _res = + assert conn + |> get("/api/v1/instance") + |> json_response_and_validate_schema(200) + + assert res = %{"thumbnail" => "https://example.com/media/new_thumbnail.jpg"} + end + + test "Concurrent Limiter", %{conn: conn} do + clear_config([ConcurrentLimiter]) + + params = %{ + "group" => ":pleroma", + "key" => "ConcurrentLimiter", + "value" => [ + %{ + "tuple" => [ + "Pleroma.Web.RichMedia.Helpers", + [ + %{"tuple" => [":max_running", 6]}, + %{"tuple" => [":max_waiting", 6]} + ] + ] + }, + %{ + "tuple" => [ + "Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy", + [ + %{"tuple" => [":max_running", 7]}, + %{"tuple" => [":max_waiting", 7]} + ] + ] + } + ] + } + + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{"configs" => [params]}) + |> json_response_and_validate_schema(200) + end end describe "GET /api/pleroma/admin/config/descriptions" do diff --git a/test/pleroma/web/admin_api/controllers/frontend_controller_test.exs b/test/pleroma/web/admin_api/controllers/frontend_controller_test.exs index 94873f6db..bc827cc12 100644 --- a/test/pleroma/web/admin_api/controllers/frontend_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/frontend_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.FrontendControllerTest do diff --git a/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs b/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs index ce867dd0e..e100f6929 100644 --- a/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do diff --git a/test/pleroma/web/admin_api/controllers/invite_controller_test.exs b/test/pleroma/web/admin_api/controllers/invite_controller_test.exs index ab186c5e7..6366061c8 100644 --- a/test/pleroma/web/admin_api/controllers/invite_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/invite_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.InviteControllerTest do @@ -7,7 +7,6 @@ defmodule Pleroma.Web.AdminAPI.InviteControllerTest do import Pleroma.Factory - alias Pleroma.Config alias Pleroma.Repo alias Pleroma.UserInviteToken @@ -119,8 +118,8 @@ test "email with +", %{conn: conn, admin: admin} do setup do: clear_config([:instance, :invites_enabled]) test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do - Config.put([:instance, :registrations_open], false) - Config.put([:instance, :invites_enabled], false) + clear_config([:instance, :registrations_open], false) + clear_config([:instance, :invites_enabled], false) conn = conn @@ -138,8 +137,8 @@ test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do end test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do - Config.put([:instance, :registrations_open], true) - Config.put([:instance, :invites_enabled], true) + clear_config([:instance, :registrations_open], true) + clear_config([:instance, :invites_enabled], true) conn = conn diff --git a/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs b/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs index f243d1fb2..5d872901e 100644 --- a/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do @@ -12,10 +12,6 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do setup do: clear_config([:media_proxy]) - setup do - on_exit(fn -> Cachex.clear(:banned_urls_cache) end) - end - setup do admin = insert(:user, is_admin: true) token = insert(:oauth_admin_token, user: admin) @@ -25,9 +21,9 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do |> assign(:user, admin) |> assign(:token, token) - Config.put([:media_proxy, :enabled], true) - Config.put([:media_proxy, :invalidation, :enabled], true) - Config.put([:media_proxy, :invalidation, :provider], MediaProxy.Invalidation.Script) + clear_config([:media_proxy, :enabled], true) + clear_config([:media_proxy, :invalidation, :enabled], true) + clear_config([:media_proxy, :invalidation, :provider], MediaProxy.Invalidation.Script) {:ok, %{admin: admin, token: token, conn: conn}} end diff --git a/test/pleroma/web/admin_api/controllers/o_auth_app_controller_test.exs b/test/pleroma/web/admin_api/controllers/o_auth_app_controller_test.exs index f388375d1..8c7b63f34 100644 --- a/test/pleroma/web/admin_api/controllers/o_auth_app_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/o_auth_app_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.OAuthAppControllerTest do diff --git a/test/pleroma/web/admin_api/controllers/relay_controller_test.exs b/test/pleroma/web/admin_api/controllers/relay_controller_test.exs index 379067a62..11a480cc0 100644 --- a/test/pleroma/web/admin_api/controllers/relay_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/relay_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.RelayControllerTest do diff --git a/test/pleroma/web/admin_api/controllers/report_controller_test.exs b/test/pleroma/web/admin_api/controllers/report_controller_test.exs index cbfc2e7b0..6a2986b5f 100644 --- a/test/pleroma/web/admin_api/controllers/report_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/report_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ReportControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true import Pleroma.Factory diff --git a/test/pleroma/web/admin_api/controllers/status_controller_test.exs b/test/pleroma/web/admin_api/controllers/status_controller_test.exs index a18ef9e4b..3fdf23ba2 100644 --- a/test/pleroma/web/admin_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/status_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.StatusControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true import Pleroma.Factory @@ -47,8 +47,8 @@ test "shows activity", %{conn: conn} do assert account["id"] == actor.id assert account["nickname"] == actor.nickname - assert account["deactivated"] == actor.deactivated - assert account["confirmation_pending"] == actor.confirmation_pending + assert account["is_active"] == actor.is_active + assert account["is_confirmed"] == actor.is_confirmed end end diff --git a/test/pleroma/web/admin_api/controllers/user_controller_test.exs b/test/pleroma/web/admin_api/controllers/user_controller_test.exs index 5705306c7..31319b5e5 100644 --- a/test/pleroma/web/admin_api/controllers/user_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/user_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.UserControllerTest do @@ -44,107 +44,50 @@ test "with valid `admin_token` query parameter, skips OAuth scopes check" do conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123") - assert json_response(conn, 200) + assert json_response_and_validate_schema(conn, 200) end - describe "with [:auth, :enforce_oauth_admin_scope_usage]," do - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true) + test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope", + %{admin: admin} do + user = insert(:user) + url = "/api/pleroma/admin/users/#{user.nickname}" - test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope", - %{admin: admin} do - user = insert(:user) - url = "/api/pleroma/admin/users/#{user.nickname}" + good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"]) + good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"]) + good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"]) - good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"]) - good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"]) - good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"]) + bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"]) + bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"]) + bad_token3 = nil - bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"]) - bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"]) - bad_token3 = nil + for good_token <- [good_token1, good_token2, good_token3] do + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, good_token) + |> get(url) - for good_token <- [good_token1, good_token2, good_token3] do - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, good_token) - |> get(url) - - assert json_response(conn, 200) - end - - for good_token <- [good_token1, good_token2, good_token3] do - conn = - build_conn() - |> assign(:user, nil) - |> assign(:token, good_token) - |> get(url) - - assert json_response(conn, :forbidden) - end - - for bad_token <- [bad_token1, bad_token2, bad_token3] do - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, bad_token) - |> get(url) - - assert json_response(conn, :forbidden) - end + assert json_response_and_validate_schema(conn, 200) end - end - describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) + for good_token <- [good_token1, good_token2, good_token3] do + conn = + build_conn() + |> assign(:user, nil) + |> assign(:token, good_token) + |> get(url) - test "GET /api/pleroma/admin/users/:nickname requires " <> - "read:accounts or admin:read:accounts or broader scope", - %{admin: admin} do - user = insert(:user) - url = "/api/pleroma/admin/users/#{user.nickname}" + assert json_response(conn, :forbidden) + end - good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"]) - good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"]) - good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"]) - good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"]) - good_token5 = insert(:oauth_token, user: admin, scopes: ["read"]) + for bad_token <- [bad_token1, bad_token2, bad_token3] do + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, bad_token) + |> get(url) - good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5] - - bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"]) - bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"]) - bad_token3 = nil - - for good_token <- good_tokens do - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, good_token) - |> get(url) - - assert json_response(conn, 200) - end - - for good_token <- good_tokens do - conn = - build_conn() - |> assign(:user, nil) - |> assign(:token, good_token) - |> get(url) - - assert json_response(conn, :forbidden) - end - - for bad_token <- [bad_token1, bad_token2, bad_token3] do - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, bad_token) - |> get(url) - - assert json_response(conn, :forbidden) - end + assert json_response_and_validate_schema(conn, :forbidden) end end @@ -169,7 +112,7 @@ test "single user", %{admin: admin, conn: conn} do assert user.note_count == 1 assert user.follower_count == 1 assert user.following_count == 1 - refute user.deactivated + assert user.is_active with_mock Pleroma.Web.Federator, publish: fn _ -> nil end, @@ -181,17 +124,17 @@ test "single user", %{admin: admin, conn: conn} do ObanHelpers.perform_all() - assert User.get_by_nickname(user.nickname).deactivated + refute User.get_by_nickname(user.nickname).is_active log_entry = Repo.one(ModerationLog) assert ModerationLog.get_log_entry_message(log_entry) == "@#{admin.nickname} deleted users: @#{user.nickname}" - assert json_response(conn, 200) == [user.nickname] + assert json_response_and_validate_schema(conn, 200) == [user.nickname] user = Repo.get(User, user.id) - assert user.deactivated + refute user.is_active assert user.avatar == %{} assert user.banner == %{} @@ -209,28 +152,30 @@ test "multiple users", %{admin: admin, conn: conn} do user_one = insert(:user) user_two = insert(:user) - conn = + response = conn |> put_req_header("accept", "application/json") + |> put_req_header("content-type", "application/json") |> delete("/api/pleroma/admin/users", %{ nicknames: [user_one.nickname, user_two.nickname] }) + |> json_response_and_validate_schema(200) log_entry = Repo.one(ModerationLog) assert ModerationLog.get_log_entry_message(log_entry) == "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}" - response = json_response(conn, 200) assert response -- [user_one.nickname, user_two.nickname] == [] end end describe "/api/pleroma/admin/users" do test "Create", %{conn: conn} do - conn = + response = conn |> put_req_header("accept", "application/json") + |> put_req_header("content-type", "application/json") |> post("/api/pleroma/admin/users", %{ "users" => [ %{ @@ -245,8 +190,9 @@ test "Create", %{conn: conn} do } ] }) + |> json_response_and_validate_schema(200) + |> Enum.map(&Map.get(&1, "type")) - response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type")) assert response == ["success", "success"] log_entry = Repo.one(ModerationLog) @@ -260,6 +206,7 @@ test "Cannot create user with existing email", %{conn: conn} do conn = conn |> put_req_header("accept", "application/json") + |> put_req_header("content-type", "application/json") |> post("/api/pleroma/admin/users", %{ "users" => [ %{ @@ -270,7 +217,7 @@ test "Cannot create user with existing email", %{conn: conn} do ] }) - assert json_response(conn, 409) == [ + assert json_response_and_validate_schema(conn, 409) == [ %{ "code" => 409, "data" => %{ @@ -289,6 +236,7 @@ test "Cannot create user with existing nickname", %{conn: conn} do conn = conn |> put_req_header("accept", "application/json") + |> put_req_header("content-type", "application/json") |> post("/api/pleroma/admin/users", %{ "users" => [ %{ @@ -299,7 +247,7 @@ test "Cannot create user with existing nickname", %{conn: conn} do ] }) - assert json_response(conn, 409) == [ + assert json_response_and_validate_schema(conn, 409) == [ %{ "code" => 409, "data" => %{ @@ -318,6 +266,7 @@ test "Multiple user creation works in transaction", %{conn: conn} do conn = conn |> put_req_header("accept", "application/json") + |> put_req_header("content-type", "application/json") |> post("/api/pleroma/admin/users", %{ "users" => [ %{ @@ -333,7 +282,7 @@ test "Multiple user creation works in transaction", %{conn: conn} do ] }) - assert json_response(conn, 409) == [ + assert json_response_and_validate_schema(conn, 409) == [ %{ "code" => 409, "data" => %{ @@ -364,7 +313,7 @@ test "Show", %{conn: conn} do conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") - assert user_response(user) == json_response(conn, 200) + assert user_response(user) == json_response_and_validate_schema(conn, 200) end test "when the user doesn't exist", %{conn: conn} do @@ -372,7 +321,7 @@ test "when the user doesn't exist", %{conn: conn} do conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") - assert %{"error" => "Not found"} == json_response(conn, 404) + assert %{"error" => "Not found"} == json_response_and_validate_schema(conn, 404) end end @@ -383,6 +332,7 @@ test "allows to force-follow another user", %{admin: admin, conn: conn} do conn |> put_req_header("accept", "application/json") + |> put_req_header("content-type", "application/json") |> post("/api/pleroma/admin/users/follow", %{ "follower" => follower.nickname, "followed" => user.nickname @@ -409,6 +359,7 @@ test "allows to force-unfollow another user", %{admin: admin, conn: conn} do conn |> put_req_header("accept", "application/json") + |> put_req_header("content-type", "application/json") |> post("/api/pleroma/admin/users/unfollow", %{ "follower" => follower.nickname, "followed" => user.nickname @@ -429,7 +380,7 @@ test "allows to force-unfollow another user", %{admin: admin, conn: conn} do describe "GET /api/pleroma/admin/users" do test "renders users array for the first page", %{conn: conn, admin: admin} do user = insert(:user, local: false, tags: ["foo", "bar"]) - user2 = insert(:user, approval_pending: true, registration_reason: "I'm a chill dude") + user2 = insert(:user, is_approved: false, registration_reason: "I'm a chill dude") conn = get(conn, "/api/pleroma/admin/users?page=1") @@ -444,7 +395,7 @@ test "renders users array for the first page", %{conn: conn, admin: admin} do user2, %{ "local" => true, - "approval_pending" => true, + "is_approved" => false, "registration_reason" => "I'm a chill dude", "actor_type" => "Person" } @@ -452,7 +403,7 @@ test "renders users array for the first page", %{conn: conn, admin: admin} do ] |> Enum.sort_by(& &1["nickname"]) - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 3, "page_size" => 50, "users" => users @@ -467,7 +418,7 @@ test "pagination works correctly with service users", %{conn: conn} do assert %{"count" => 26, "page_size" => 10, "users" => users1} = conn |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"}) - |> json_response(200) + |> json_response_and_validate_schema(200) assert Enum.count(users1) == 10 assert service1 not in users1 @@ -475,7 +426,7 @@ test "pagination works correctly with service users", %{conn: conn} do assert %{"count" => 26, "page_size" => 10, "users" => users2} = conn |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"}) - |> json_response(200) + |> json_response_and_validate_schema(200) assert Enum.count(users2) == 10 assert service1 not in users2 @@ -483,7 +434,7 @@ test "pagination works correctly with service users", %{conn: conn} do assert %{"count" => 26, "page_size" => 10, "users" => users3} = conn |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"}) - |> json_response(200) + |> json_response_and_validate_schema(200) assert Enum.count(users3) == 6 assert service1 not in users3 @@ -494,7 +445,7 @@ test "renders empty array for the second page", %{conn: conn} do conn = get(conn, "/api/pleroma/admin/users?page=2") - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 2, "page_size" => 50, "users" => [] @@ -506,7 +457,7 @@ test "regular search", %{conn: conn} do conn = get(conn, "/api/pleroma/admin/users?query=bo") - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 1, "page_size" => 50, "users" => [user_response(user, %{"local" => true})] @@ -519,7 +470,7 @@ test "search by domain", %{conn: conn} do conn = get(conn, "/api/pleroma/admin/users?query=domain.com") - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 1, "page_size" => 50, "users" => [user_response(user)] @@ -532,7 +483,7 @@ test "search by full nickname", %{conn: conn} do conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com") - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 1, "page_size" => 50, "users" => [user_response(user)] @@ -545,7 +496,7 @@ test "search by display name", %{conn: conn} do conn = get(conn, "/api/pleroma/admin/users?name=display") - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 1, "page_size" => 50, "users" => [user_response(user)] @@ -558,7 +509,7 @@ test "search by email", %{conn: conn} do conn = get(conn, "/api/pleroma/admin/users?email=email@example.com") - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 1, "page_size" => 50, "users" => [user_response(user)] @@ -571,7 +522,7 @@ test "regular search with page size", %{conn: conn} do conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1") - assert json_response(conn1, 200) == %{ + assert json_response_and_validate_schema(conn1, 200) == %{ "count" => 2, "page_size" => 1, "users" => [user_response(user)] @@ -579,7 +530,7 @@ test "regular search with page size", %{conn: conn} do conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2") - assert json_response(conn2, 200) == %{ + assert json_response_and_validate_schema(conn2, 200) == %{ "count" => 2, "page_size" => 1, "users" => [user_response(user2)] @@ -599,7 +550,7 @@ test "only local users" do |> assign(:token, token) |> get("/api/pleroma/admin/users?query=bo&filters=local") - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 1, "page_size" => 50, "users" => [user_response(user)] @@ -621,13 +572,13 @@ test "only local users with no query", %{conn: conn, admin: old_admin} do "roles" => %{"admin" => true, "moderator" => false} }), user_response(old_admin, %{ - "deactivated" => false, + "is_active" => true, "roles" => %{"admin" => true, "moderator" => false} }) ] |> Enum.sort_by(& &1["nickname"]) - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 3, "page_size" => 50, "users" => users @@ -635,22 +586,22 @@ test "only local users with no query", %{conn: conn, admin: old_admin} do end test "only unconfirmed users", %{conn: conn} do - sad_user = insert(:user, nickname: "sadboy", confirmation_pending: true) - old_user = insert(:user, nickname: "oldboy", confirmation_pending: true) + sad_user = insert(:user, nickname: "sadboy", is_confirmed: false) + old_user = insert(:user, nickname: "oldboy", is_confirmed: false) - insert(:user, nickname: "happyboy", approval_pending: false) - insert(:user, confirmation_pending: false) + insert(:user, nickname: "happyboy", is_approved: true) + insert(:user, is_confirmed: true) result = conn |> get("/api/pleroma/admin/users?filters=unconfirmed") - |> json_response(200) + |> json_response_and_validate_schema(200) users = Enum.map([old_user, sad_user], fn user -> user_response(user, %{ - "confirmation_pending" => true, - "approval_pending" => false + "is_confirmed" => false, + "is_approved" => true }) end) |> Enum.sort_by(& &1["nickname"]) @@ -662,22 +613,22 @@ test "only unapproved users", %{conn: conn} do user = insert(:user, nickname: "sadboy", - approval_pending: true, + is_approved: false, registration_reason: "Plz let me in!" ) - insert(:user, nickname: "happyboy", approval_pending: false) + insert(:user, nickname: "happyboy", is_approved: true) conn = get(conn, "/api/pleroma/admin/users?filters=need_approval") users = [ user_response( user, - %{"approval_pending" => true, "registration_reason" => "Plz let me in!"} + %{"is_approved" => false, "registration_reason" => "Plz let me in!"} ) ] - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 1, "page_size" => 50, "users" => users @@ -694,17 +645,17 @@ test "load only admins", %{conn: conn, admin: admin} do users = [ user_response(admin, %{ - "deactivated" => false, + "is_active" => true, "roles" => %{"admin" => true, "moderator" => false} }), user_response(second_admin, %{ - "deactivated" => false, + "is_active" => true, "roles" => %{"admin" => true, "moderator" => false} }) ] |> Enum.sort_by(& &1["nickname"]) - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 2, "page_size" => 50, "users" => users @@ -718,12 +669,12 @@ test "load only moderators", %{conn: conn} do conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator") - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 1, "page_size" => 50, "users" => [ user_response(moderator, %{ - "deactivated" => false, + "is_active" => true, "roles" => %{"admin" => false, "moderator" => true} }) ] @@ -739,8 +690,8 @@ test "load users with actor_type is Person", %{admin: admin, conn: conn} do response = conn - |> get(user_path(conn, :list), %{actor_types: ["Person"]}) - |> json_response(200) + |> get(user_path(conn, :index), %{actor_types: ["Person"]}) + |> json_response_and_validate_schema(200) users = [ @@ -762,8 +713,8 @@ test "load users with actor_type is Person and Service", %{admin: admin, conn: c response = conn - |> get(user_path(conn, :list), %{actor_types: ["Person", "Service"]}) - |> json_response(200) + |> get(user_path(conn, :index), %{actor_types: ["Person", "Service"]}) + |> json_response_and_validate_schema(200) users = [ @@ -785,8 +736,8 @@ test "load users with actor_type is Service", %{conn: conn} do response = conn - |> get(user_path(conn, :list), %{actor_types: ["Service"]}) - |> json_response(200) + |> get(user_path(conn, :index), %{actor_types: ["Service"]}) + |> json_response_and_validate_schema(200) users = [user_response(user_service, %{"actor_type" => "Service"})] @@ -808,7 +759,7 @@ test "load users with tags list", %{conn: conn} do ] |> Enum.sort_by(& &1["nickname"]) - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 2, "page_size" => 50, "users" => users @@ -816,8 +767,8 @@ test "load users with tags list", %{conn: conn} do end test "`active` filters out users pending approval", %{token: token} do - insert(:user, approval_pending: true) - %{id: user_id} = insert(:user, approval_pending: false) + insert(:user, is_approved: false) + %{id: user_id} = insert(:user, is_approved: true) %{id: admin_id} = token.user conn = @@ -833,16 +784,16 @@ test "`active` filters out users pending approval", %{token: token} do %{"id" => ^admin_id}, %{"id" => ^user_id} ] - } = json_response(conn, 200) + } = json_response_and_validate_schema(conn, 200) end test "it works with multiple filters" do admin = insert(:user, nickname: "john", is_admin: true) token = insert(:oauth_admin_token, user: admin) - user = insert(:user, nickname: "bob", local: false, deactivated: true) + user = insert(:user, nickname: "bob", local: false, is_active: false) - insert(:user, nickname: "ken", local: true, deactivated: true) - insert(:user, nickname: "bobb", local: false, deactivated: false) + insert(:user, nickname: "ken", local: true, is_active: false) + insert(:user, nickname: "bobb", local: false, is_active: true) conn = build_conn() @@ -850,7 +801,7 @@ test "it works with multiple filters" do |> assign(:token, token) |> get("/api/pleroma/admin/users?filters=deactivated,external") - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 1, "page_size" => 50, "users" => [user_response(user)] @@ -862,7 +813,7 @@ test "it omits relay user", %{admin: admin, conn: conn} do conn = get(conn, "/api/pleroma/admin/users") - assert json_response(conn, 200) == %{ + assert json_response_and_validate_schema(conn, 200) == %{ "count" => 1, "page_size" => 50, "users" => [ @@ -873,18 +824,19 @@ test "it omits relay user", %{admin: admin, conn: conn} do end test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do - user_one = insert(:user, deactivated: true) - user_two = insert(:user, deactivated: true) + user_one = insert(:user, is_active: false) + user_two = insert(:user, is_active: false) conn = - patch( - conn, + conn + |> put_req_header("content-type", "application/json") + |> patch( "/api/pleroma/admin/users/activate", %{nicknames: [user_one.nickname, user_two.nickname]} ) - response = json_response(conn, 200) - assert Enum.map(response["users"], & &1["deactivated"]) == [false, false] + response = json_response_and_validate_schema(conn, 200) + assert Enum.map(response["users"], & &1["is_active"]) == [true, true] log_entry = Repo.one(ModerationLog) @@ -893,18 +845,19 @@ test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do end test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do - user_one = insert(:user, deactivated: false) - user_two = insert(:user, deactivated: false) + user_one = insert(:user, is_active: true) + user_two = insert(:user, is_active: true) conn = - patch( - conn, + conn + |> put_req_header("content-type", "application/json") + |> patch( "/api/pleroma/admin/users/deactivate", %{nicknames: [user_one.nickname, user_two.nickname]} ) - response = json_response(conn, 200) - assert Enum.map(response["users"], & &1["deactivated"]) == [true, true] + response = json_response_and_validate_schema(conn, 200) + assert Enum.map(response["users"], & &1["is_active"]) == [false, false] log_entry = Repo.one(ModerationLog) @@ -913,18 +866,19 @@ test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do end test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do - user_one = insert(:user, approval_pending: true) - user_two = insert(:user, approval_pending: true) + user_one = insert(:user, is_approved: false) + user_two = insert(:user, is_approved: false) conn = - patch( - conn, + conn + |> put_req_header("content-type", "application/json") + |> patch( "/api/pleroma/admin/users/approve", %{nicknames: [user_one.nickname, user_two.nickname]} ) - response = json_response(conn, 200) - assert Enum.map(response["users"], & &1["approval_pending"]) == [false, false] + response = json_response_and_validate_schema(conn, 200) + assert Enum.map(response["users"], & &1["is_approved"]) == [true, true] log_entry = Repo.one(ModerationLog) @@ -935,12 +889,15 @@ test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do user = insert(:user) - conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation") + conn = + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation") - assert json_response(conn, 200) == + assert json_response_and_validate_schema(conn, 200) == user_response( user, - %{"deactivated" => !user.deactivated} + %{"is_active" => !user.is_active} ) log_entry = Repo.one(ModerationLog) @@ -951,16 +908,17 @@ test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admi defp user_response(user, attrs \\ %{}) do %{ - "deactivated" => user.deactivated, + "is_active" => user.is_active, "id" => user.id, + "email" => user.email, "nickname" => user.nickname, "roles" => %{"admin" => false, "moderator" => false}, "local" => user.local, "tags" => [], "avatar" => User.avatar_url(user) |> MediaProxy.url(), "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, + "is_confirmed" => true, + "is_approved" => true, "url" => user.ap_id, "registration_reason" => nil, "actor_type" => "Person" diff --git a/test/pleroma/web/admin_api/search_test.exs b/test/pleroma/web/admin_api/search_test.exs index 9bc58640c..b8eeec65b 100644 --- a/test/pleroma/web/admin_api/search_test.exs +++ b/test/pleroma/web/admin_api/search_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.SearchTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Web.AdminAPI.Search @@ -47,9 +47,9 @@ test "it returns local/external users" do end test "it returns active/deactivated users" do - insert(:user, deactivated: true) - insert(:user, deactivated: true) - insert(:user, deactivated: false) + insert(:user, is_active: false) + insert(:user, is_active: false) + insert(:user, is_active: true) {:ok, _results, active_count} = Search.user(%{ @@ -70,7 +70,7 @@ test "it returns active/deactivated users" do test "it returns specific user" do insert(:user) insert(:user) - user = insert(:user, nickname: "bob", local: true, deactivated: false) + user = insert(:user, nickname: "bob", local: true, is_active: true) {:ok, _results, total_count} = Search.user(%{query: ""}) @@ -182,7 +182,7 @@ test "it returns user by email" do end test "it returns unapproved user" do - unapproved = insert(:user, approval_pending: true) + unapproved = insert(:user, is_approved: false) insert(:user) insert(:user) @@ -193,7 +193,7 @@ test "it returns unapproved user" do end test "it returns unconfirmed user" do - unconfirmed = insert(:user, confirmation_pending: true) + unconfirmed = insert(:user, is_confirmed: false) insert(:user) insert(:user) diff --git a/test/pleroma/web/admin_api/views/account_view_test.exs b/test/pleroma/web/admin_api/views/account_view_test.exs new file mode 100644 index 000000000..025726c73 --- /dev/null +++ b/test/pleroma/web/admin_api/views/account_view_test.exs @@ -0,0 +1,16 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.AccountViewTest do + use Pleroma.DataCase, async: true + import Pleroma.Factory + alias Pleroma.Web.AdminAPI.AccountView + + describe "show.json" do + test "renders the user's email" do + user = insert(:user, email: "yolo@yolofam.tld") + assert %{"email" => "yolo@yolofam.tld"} = AccountView.render("show.json", %{user: user}) + end + end +end diff --git a/test/pleroma/web/admin_api/views/moderation_log_view_test.exs b/test/pleroma/web/admin_api/views/moderation_log_view_test.exs index e6c5aaa7f..4efe4c4c8 100644 --- a/test/pleroma/web/admin_api/views/moderation_log_view_test.exs +++ b/test/pleroma/web/admin_api/views/moderation_log_view_test.exs @@ -1,14 +1,15 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ModerationLogViewTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.AdminAPI.ModerationLogView describe "renders `report_note_delete` log messages" do setup do log1 = %Pleroma.ModerationLog{ + id: 1, data: %{ "action" => "report_note_delete", "actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"}, @@ -21,6 +22,7 @@ defmodule Pleroma.Web.AdminAPI.ModerationLogViewTest do } log2 = %Pleroma.ModerationLog{ + id: 2, data: %{ "action" => "report_note_delete", "actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"}, @@ -42,6 +44,7 @@ test "renders `report_note_delete` log messages", %{log1: log1, log2: log2} do ) == %{ items: [ %{ + id: 1, data: %{ "action" => "report_note_delete", "actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"}, @@ -59,6 +62,7 @@ test "renders `report_note_delete` log messages", %{log1: log1, log2: log2} do time: 1_605_622_400 }, %{ + id: 2, data: %{ "action" => "report_note_delete", "actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"}, @@ -82,6 +86,7 @@ test "renders `report_note_delete` log messages", %{log1: log1, log2: log2} do test "renders `report_note_delete` log message", %{log1: log} do assert ModerationLogView.render("show.json", %{log_entry: log}) == %{ + id: 1, data: %{ "action" => "report_note_delete", "actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"}, diff --git a/test/pleroma/web/admin_api/views/report_view_test.exs b/test/pleroma/web/admin_api/views/report_view_test.exs index 5a02292be..093e2d95d 100644 --- a/test/pleroma/web/admin_api/views/report_view_test.exs +++ b/test/pleroma/web/admin_api/views/report_view_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.ReportViewTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory @@ -143,4 +143,29 @@ test "doesn't error out when the user doesn't exists" do assert %{} = ReportView.render("show.json", Report.extract_report_info(activity)) end + + test "reports are ordered newest first" do + user = insert(:user) + other_user = insert(:user) + + {:ok, report1} = + CommonAPI.report(user, %{ + account_id: other_user.id, + comment: "first report" + }) + + {:ok, report2} = + CommonAPI.report(user, %{ + account_id: other_user.id, + comment: "second report" + }) + + %{reports: rendered} = + ReportView.render("index.json", + reports: Pleroma.Web.ActivityPub.Utils.get_reports(%{}, 1, 50) + ) + + assert report2.id == rendered |> Enum.at(0) |> Map.get(:id) + assert report1.id == rendered |> Enum.at(1) |> Map.get(:id) + end end diff --git a/test/pleroma/web/api_spec/schema_examples_test.exs b/test/pleroma/web/api_spec/schema_examples_test.exs index f00e834fc..981890d77 100644 --- a/test/pleroma/web/api_spec/schema_examples_test.exs +++ b/test/pleroma/web/api_spec/schema_examples_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ApiSpec.SchemaExamplesTest do diff --git a/test/pleroma/web/auth/auth_controller_test.exs b/test/pleroma/web/auth/auth_controller_test.exs index 498554060..a869389e3 100644 --- a/test/pleroma/web/auth/auth_controller_test.exs +++ b/test/pleroma/web/auth/auth_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.AuthControllerTest do diff --git a/test/pleroma/web/auth/authenticator_test.exs b/test/pleroma/web/auth/authenticator_test.exs index d54253343..e1f30e835 100644 --- a/test/pleroma/web/auth/authenticator_test.exs +++ b/test/pleroma/web/auth/authenticator_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.AuthenticatorTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Web.Auth.Authenticator import Pleroma.Factory diff --git a/test/pleroma/web/auth/basic_auth_test.exs b/test/pleroma/web/auth/basic_auth_test.exs index bf6e3d2fc..2816aae4c 100644 --- a/test/pleroma/web/auth/basic_auth_test.exs +++ b/test/pleroma/web/auth/basic_auth_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.BasicAuthTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true import Pleroma.Factory @@ -11,7 +11,7 @@ test "with HTTP Basic Auth used, grants access to OAuth scope-restricted endpoin conn: conn } do user = insert(:user) - assert Pbkdf2.verify_pass("test", user.password_hash) + assert Pleroma.Password.Pbkdf2.verify_pass("test", user.password_hash) basic_auth_contents = (URI.encode_www_form(user.nickname) <> ":" <> URI.encode_www_form("test")) diff --git a/test/pleroma/web/auth/pleroma_authenticator_test.exs b/test/pleroma/web/auth/pleroma_authenticator_test.exs index 1ba0dfecc..b1397c523 100644 --- a/test/pleroma/web/auth/pleroma_authenticator_test.exs +++ b/test/pleroma/web/auth/pleroma_authenticator_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Web.Auth.PleromaAuthenticator import Pleroma.Factory @@ -11,7 +11,13 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do setup do password = "testpassword" name = "AgentSmith" - user = insert(:user, nickname: name, password_hash: Pbkdf2.hash_pwd_salt(password)) + + user = + insert(:user, + nickname: name, + password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password) + ) + {:ok, [user: user, name: name, password: password]} end diff --git a/test/pleroma/web/auth/totp_authenticator_test.exs b/test/pleroma/web/auth/totp_authenticator_test.exs index 84d4cd840..ac4209f2d 100644 --- a/test/pleroma/web/auth/totp_authenticator_test.exs +++ b/test/pleroma/web/auth/totp_authenticator_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.TOTPAuthenticatorTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.MFA alias Pleroma.MFA.BackupCodes @@ -34,7 +34,7 @@ test "checks backup codes" do hashed_codes = backup_codes - |> Enum.map(&Pbkdf2.hash_pwd_salt(&1)) + |> Enum.map(&Pleroma.Password.Pbkdf2.hash_pwd_salt(&1)) user = insert(:user, diff --git a/test/pleroma/web/chat_channel_test.exs b/test/pleroma/web/chat_channel_test.exs index 32170873d..29999701c 100644 --- a/test/pleroma/web/chat_channel_test.exs +++ b/test/pleroma/web/chat_channel_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ChatChannelTest do @@ -33,7 +33,7 @@ test "it ignores messages of length zero", %{socket: socket} do end test "it ignores messages above a certain length", %{socket: socket} do - Pleroma.Config.put([:instance, :chat_limit], 2) + clear_config([:instance, :chat_limit], 2) push(socket, "new_msg", %{"text" => "123"}) refute_broadcast("new_msg", %{text: "123"}) end diff --git a/test/pleroma/web/common_api/utils_test.exs b/test/pleroma/web/common_api/utils_test.exs index 8c79a9a83..b0e567ff0 100644 --- a/test/pleroma/web/common_api/utils_test.exs +++ b/test/pleroma/web/common_api/utils_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.CommonAPI.UtilsTest do diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs index c1b1af073..be94c93c2 100644 --- a/test/pleroma/web/common_api_test.exs +++ b/test/pleroma/web/common_api_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.CommonAPITest do @@ -25,6 +25,11 @@ defmodule Pleroma.Web.CommonAPITest do require Pleroma.Constants + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + setup do: clear_config([:instance, :safe_dm_mentions]) setup do: clear_config([:instance, :limit]) setup do: clear_config([:instance, :max_pinned_statuses]) @@ -39,7 +44,7 @@ test "it posts a poll" do poll: %{expires_in: 600, options: ["reimu", "marisa"]} }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert object.data["type"] == "Question" assert object.data["oneOf"] |> length() == 2 @@ -174,7 +179,7 @@ test "it adds html newlines" do assert other_user.ap_id not in activity.recipients - object = Object.normalize(activity, false) + object = Object.normalize(activity, fetch: false) assert object.data["content"] == "uguu
uguuu" end @@ -194,7 +199,7 @@ test "it linkifies" do assert other_user.ap_id not in activity.recipients - object = Object.normalize(activity, false) + object = Object.normalize(activity, fetch: false) assert object.data["content"] == "https://example.org is the site of Object.prune() with_mock Pleroma.Web.Federator, @@ -475,7 +480,7 @@ test "with the safe_dm_mention option set, it does not mention people beyond the jafnhar = insert(:user) tridi = insert(:user) - Pleroma.Config.put([:instance, :safe_dm_mentions], true) + clear_config([:instance, :safe_dm_mentions], true) {:ok, activity} = CommonAPI.post(har, %{ @@ -491,21 +496,55 @@ test "it de-duplicates tags" do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU"}) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) - assert object.data["tag"] == ["2hu"] + assert Object.tags(object) == ["2hu"] end test "it adds emoji in the object" do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: ":firefox:"}) - assert Object.normalize(activity).data["emoji"]["firefox"] + assert Object.normalize(activity, fetch: false).data["emoji"]["firefox"] end describe "posting" do + test "it adds an emoji on an external site" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey :external_emoji:"}) + + assert %{"external_emoji" => url} = Object.normalize(activity).data["emoji"] + assert url == "https://example.com/emoji.png" + + {:ok, activity} = CommonAPI.post(user, %{status: "hey :blank:"}) + + assert %{"blank" => url} = Object.normalize(activity).data["emoji"] + assert url == "#{Pleroma.Web.base_url()}/emoji/blank.png" + end + + test "it copies emoji from the subject of the parent post" do + %Object{} = + object = + Object.normalize("https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f", + fetch: true + ) + + activity = Activity.get_create_by_object_ap_id(object.data["id"]) + user = insert(:user) + + {:ok, reply_activity} = + CommonAPI.post(user, %{ + in_reply_to_id: activity.id, + status: ":joker_disapprove:", + spoiler_text: ":joker_smile:" + }) + + assert Object.normalize(reply_activity).data["emoji"][":joker_smile:"] + refute Object.normalize(reply_activity).data["emoji"][":joker_disapprove:"] + end + test "deactivated users can't post" do - user = insert(:user, deactivated: true) + user = insert(:user, is_active: false) assert {:error, _} = CommonAPI.post(user, %{status: "ye"}) end @@ -539,7 +578,7 @@ test "it filters out obviously bad tags when accepting a post as HTML" do content_type: "text/html" }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert object.data["content"] == "

2hu

alert('xss')" assert object.data["source"] == post @@ -556,7 +595,7 @@ test "it filters out obviously bad tags when accepting a post as Markdown" do content_type: "text/markdown" }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert object.data["content"] == "

2hu

" assert object.data["source"] == post @@ -629,7 +668,7 @@ test "it returns error when status is empty and no attachments" do end test "it validates character limits are correctly enforced" do - Pleroma.Config.put([:instance, :limit], 5) + clear_config([:instance, :limit], 5) user = insert(:user) @@ -731,6 +770,22 @@ test "repeating a status privately" do refute Visibility.visible_for_user?(announce_activity, nil) end + test "author can repeat own private statuses" do + author = insert(:user) + follower = insert(:user) + CommonAPI.follow(follower, author) + + {:ok, activity} = CommonAPI.post(author, %{status: "cofe", visibility: "private"}) + + {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, author) + + assert Visibility.is_private?(announce_activity) + refute Visibility.visible_for_user?(announce_activity, nil) + + assert Visibility.visible_for_user?(activity, follower) + assert {:error, :not_found} = CommonAPI.repeat(activity.id, follower) + end + test "favoriting a status" do user = insert(:user) other_user = insert(:user) @@ -764,7 +819,7 @@ test "favoriting a status twice returns ok, but without the like activity" do describe "pinned statuses" do setup do - Pleroma.Config.put([:instance, :max_pinned_statuses], 1) + clear_config([:instance, :max_pinned_statuses], 1) user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"}) @@ -772,13 +827,17 @@ test "favoriting a status twice returns ok, but without the like activity" do [user: user, activity: activity] end + test "activity not found error", %{user: user} do + assert {:error, :not_found} = CommonAPI.pin("id", user) + end + test "pin status", %{user: user, activity: activity} do assert {:ok, ^activity} = CommonAPI.pin(activity.id, user) - id = activity.id + %{data: %{"id" => object_id}} = Object.normalize(activity) user = refresh_record(user) - assert %User{pinned_activities: [^id]} = user + assert user.pinned_objects |> Map.keys() == [object_id] end test "pin poll", %{user: user} do @@ -790,10 +849,11 @@ test "pin poll", %{user: user} do assert {:ok, ^activity} = CommonAPI.pin(activity.id, user) - id = activity.id + %{data: %{"id" => object_id}} = Object.normalize(activity) + user = refresh_record(user) - assert %User{pinned_activities: [^id]} = user + assert user.pinned_objects |> Map.keys() == [object_id] end test "unlisted statuses can be pinned", %{user: user} do @@ -804,7 +864,7 @@ test "unlisted statuses can be pinned", %{user: user} do test "only self-authored can be pinned", %{activity: activity} do user = insert(:user) - assert {:error, "Could not pin"} = CommonAPI.pin(activity.id, user) + assert {:error, :ownership_error} = CommonAPI.pin(activity.id, user) end test "max pinned statuses", %{user: user, activity: activity_one} do @@ -814,8 +874,12 @@ test "max pinned statuses", %{user: user, activity: activity_one} do user = refresh_record(user) - assert {:error, "You have already pinned the maximum number of statuses"} = - CommonAPI.pin(activity_two.id, user) + assert {:error, :pinned_statuses_limit_reached} = CommonAPI.pin(activity_two.id, user) + end + + test "only public can be pinned", %{user: user} do + {:ok, activity} = CommonAPI.post(user, %{status: "private status", visibility: "private"}) + {:error, :visibility_error} = CommonAPI.pin(activity.id, user) end test "unpin status", %{user: user, activity: activity} do @@ -829,7 +893,7 @@ test "unpin status", %{user: user, activity: activity} do user = refresh_record(user) - assert %User{pinned_activities: []} = user + assert user.pinned_objects == %{} end test "should unpin when deleting a status", %{user: user, activity: activity} do @@ -841,7 +905,40 @@ test "should unpin when deleting a status", %{user: user, activity: activity} do user = refresh_record(user) - assert %User{pinned_activities: []} = user + assert user.pinned_objects == %{} + end + + test "ephemeral activity won't be deleted if was pinned", %{user: user} do + {:ok, activity} = CommonAPI.post(user, %{status: "Hello!", expires_in: 601}) + + assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) + + {:ok, _activity} = CommonAPI.pin(activity.id, user) + refute Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) + + user = refresh_record(user) + {:ok, _} = CommonAPI.unpin(activity.id, user) + + # recreates expiration job on unpin + assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) + end + + test "ephemeral activity deletion job won't be deleted on pinning error", %{ + user: user, + activity: activity + } do + clear_config([:instance, :max_pinned_statuses], 1) + + {:ok, _activity} = CommonAPI.pin(activity.id, user) + + {:ok, activity2} = CommonAPI.post(user, %{status: "another status", expires_in: 601}) + + assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity2.id) + + user = refresh_record(user) + {:error, :pinned_statuses_limit_reached} = CommonAPI.pin(activity2.id, user) + + assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity2.id) end end @@ -1211,7 +1308,7 @@ test "does not allow to vote twice" do poll: %{options: ["Yes", "No"], expires_in: 20} }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) {:ok, _, object} = CommonAPI.vote(other_user, object, [0]) @@ -1231,7 +1328,7 @@ test "returns a valid activity" do length: 180_000 }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert object.data["title"] == "lain radio episode 1" @@ -1250,7 +1347,7 @@ test "respects visibility=private" do visibility: "private" }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert object.data["title"] == "lain radio episode 1" diff --git a/test/pleroma/web/endpoint/metrics_exporter_test.exs b/test/pleroma/web/endpoint/metrics_exporter_test.exs index 875addc96..376e82149 100644 --- a/test/pleroma/web/endpoint/metrics_exporter_test.exs +++ b/test/pleroma/web/endpoint/metrics_exporter_test.exs @@ -1,8 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Endpoint.MetricsExporterTest do + # Modifies AppEnv, has to stay synchronous use Pleroma.Web.ConnCase alias Pleroma.Web.Endpoint.MetricsExporter diff --git a/test/pleroma/web/fallback_test.exs b/test/pleroma/web/fallback_test.exs index 46c7bad1c..512baf813 100644 --- a/test/pleroma/web/fallback_test.exs +++ b/test/pleroma/web/fallback_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.FallbackTest do diff --git a/test/pleroma/web/federator_test.exs b/test/pleroma/web/federator_test.exs index 67001add7..532ee6d30 100644 --- a/test/pleroma/web/federator_test.exs +++ b/test/pleroma/web/federator_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.FederatorTest do @@ -56,7 +56,7 @@ test "with relays deactivated, it does not publish to the relay", %{ activity: activity, relay_mock: relay_mock } do - Pleroma.Config.put([:instance, :allow_relay], false) + clear_config([:instance, :allow_relay], false) with_mocks([relay_mock]) do Federator.publish(activity) @@ -155,9 +155,9 @@ test "rejects incoming AP docs with incorrect origin" do end test "it does not crash if MRF rejects the post" do - Pleroma.Config.put([:mrf_keyword, :reject], ["lain"]) + clear_config([:mrf_keyword, :reject], ["lain"]) - Pleroma.Config.put( + clear_config( [:mrf, :policies], Pleroma.Web.ActivityPub.MRF.KeywordPolicy ) diff --git a/test/pleroma/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs index e4084b0e5..5c9201de1 100644 --- a/test/pleroma/web/feed/tag_controller_test.exs +++ b/test/pleroma/web/feed/tag_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Feed.TagControllerTest do @@ -8,7 +8,6 @@ defmodule Pleroma.Web.Feed.TagControllerTest do import Pleroma.Factory import SweetXml - alias Pleroma.Config alias Pleroma.Object alias Pleroma.Web.CommonAPI alias Pleroma.Web.Feed.FeedView @@ -16,7 +15,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do setup do: clear_config([:feed]) test "gets a feed (ATOM)", %{conn: conn} do - Config.put( + clear_config( [:feed, :post_title], %{max_length: 25, omission: "..."} ) @@ -24,7 +23,7 @@ test "gets a feed (ATOM)", %{conn: conn} do user = insert(:user) {:ok, activity1} = CommonAPI.post(user, %{status: "yeah #PleromaArt"}) - object = Object.normalize(activity1) + object = Object.normalize(activity1, fetch: false) object_data = Map.put(object.data, "attachment", [ @@ -83,7 +82,7 @@ test "gets a feed (ATOM)", %{conn: conn} do end test "gets a feed (RSS)", %{conn: conn} do - Config.put( + clear_config( [:feed, :post_title], %{max_length: 25, omission: "..."} ) @@ -91,7 +90,7 @@ test "gets a feed (RSS)", %{conn: conn} do user = insert(:user) {:ok, activity1} = CommonAPI.post(user, %{status: "yeah #PleromaArt"}) - object = Object.normalize(activity1) + object = Object.normalize(activity1, fetch: false) object_data = Map.put(object.data, "attachment", [ @@ -131,7 +130,7 @@ test "gets a feed (RSS)", %{conn: conn} do '#{Pleroma.Web.base_url()}/tags/pleromaart.rss' assert xpath(xml, ~x"//channel/webfeeds:logo/text()") == - '#{Pleroma.Web.base_url()}/static/logo.png' + '#{Pleroma.Web.base_url()}/static/logo.svg' assert xpath(xml, ~x"//channel/item/title/text()"l) == [ '42 This is :moominmamm...', @@ -147,8 +146,8 @@ test "gets a feed (RSS)", %{conn: conn} do "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4" ] - obj1 = Object.normalize(activity1) - obj2 = Object.normalize(activity2) + obj1 = Object.normalize(activity1, fetch: false) + obj2 = Object.normalize(activity2, fetch: false) assert xpath(xml, ~x"//channel/item/description/text()"sl) == [ HtmlEntities.decode(FeedView.activity_content(obj2.data)), diff --git a/test/pleroma/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs index 16f002717..408653d92 100644 --- a/test/pleroma/web/feed/user_controller_test.exs +++ b/test/pleroma/web/feed/user_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Feed.UserControllerTest do @@ -8,7 +8,6 @@ defmodule Pleroma.Web.Feed.UserControllerTest do import Pleroma.Factory import SweetXml - alias Pleroma.Config alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.CommonAPI @@ -20,7 +19,7 @@ defmodule Pleroma.Web.Feed.UserControllerTest do setup do: clear_config([:feed]) setup do - Config.put( + clear_config( [:feed, :post_title], %{max_length: 15, omission: "..."} ) @@ -58,7 +57,7 @@ defmodule Pleroma.Web.Feed.UserControllerTest do ) note_activity2 = insert(:note_activity, note: note2) - object = Object.normalize(note_activity) + object = Object.normalize(note_activity, fetch: false) [user: user, object: object, max_id: note_activity2.id] end @@ -236,7 +235,7 @@ test "with non-html / non-json format, it returns error when user is not found", setup do: clear_config([:instance, :public]) test "returns 404 for user feed", %{conn: conn} do - Config.put([:instance, :public], false) + clear_config([:instance, :public], false) user = insert(:user) {:ok, _} = CommonAPI.post(user, %{status: "test"}) diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index 3361c8669..a327c0d1d 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do @@ -29,6 +29,45 @@ test "works by id" do |> json_response_and_validate_schema(404) end + test "relationship field" do + %{conn: conn, user: user} = oauth_access(["read"]) + + other_user = insert(:user) + + response = + conn + |> get("/api/v1/accounts/#{other_user.id}") + |> json_response_and_validate_schema(200) + + assert response["id"] == other_user.id + assert response["pleroma"]["relationship"] == %{} + + assert %{"pleroma" => %{"relationship" => %{"following" => false, "followed_by" => false}}} = + conn + |> get("/api/v1/accounts/#{other_user.id}?with_relationships=true") + |> json_response_and_validate_schema(200) + + {:ok, _, %{id: other_id}} = User.follow(user, other_user) + + assert %{ + "id" => ^other_id, + "pleroma" => %{"relationship" => %{"following" => true, "followed_by" => false}} + } = + conn + |> get("/api/v1/accounts/#{other_id}?with_relationships=true") + |> json_response_and_validate_schema(200) + + {:ok, _, _} = User.follow(other_user, user) + + assert %{ + "id" => ^other_id, + "pleroma" => %{"relationship" => %{"following" => true, "followed_by" => true}} + } = + conn + |> get("/api/v1/accounts/#{other_id}?with_relationships=true") + |> json_response_and_validate_schema(200) + end + test "works by nickname" do user = insert(:user) @@ -126,7 +165,7 @@ test "returns 404 for internal.fetch actor", %{conn: conn} do end test "returns 404 for deactivated user", %{conn: conn} do - user = insert(:user, deactivated: true) + user = insert(:user, is_active: false) assert %{"error" => "Can't find user"} = conn @@ -256,7 +295,7 @@ test "works with announces that are just addressed to public", %{conn: conn} do end test "deactivated user", %{conn: conn} do - user = insert(:user, deactivated: true) + user = insert(:user, is_active: false) assert %{"error" => "Can't find user"} == conn @@ -469,6 +508,21 @@ test "muted reactions", %{user: user, conn: conn} do } ] = result end + + test "paginates a user's statuses", %{user: user, conn: conn} do + {:ok, post_1} = CommonAPI.post(user, %{status: "first post"}) + {:ok, post_2} = CommonAPI.post(user, %{status: "second post"}) + + response_1 = get(conn, "/api/v1/accounts/#{user.id}/statuses?limit=1") + assert [res] = json_response(response_1, 200) + assert res["id"] == post_2.id + + response_2 = get(conn, "/api/v1/accounts/#{user.id}/statuses?limit=1&max_id=#{res["id"]}") + assert [res] = json_response(response_2, 200) + assert res["id"] == post_1.id + + refute response_1 == response_2 + end end defp local_and_remote_activities(%{local: local, remote: remote}) do @@ -575,6 +629,45 @@ test "getting followers", %{user: user, conn: conn} do assert [%{"id" => ^user_id}] = json_response_and_validate_schema(conn, 200) end + test "following with relationship", %{conn: conn, user: user} do + other_user = insert(:user) + {:ok, %{id: id}, _} = User.follow(other_user, user) + + assert [ + %{ + "id" => ^id, + "pleroma" => %{ + "relationship" => %{ + "id" => ^id, + "following" => false, + "followed_by" => true + } + } + } + ] = + conn + |> get("/api/v1/accounts/#{user.id}/followers?with_relationships=true") + |> json_response_and_validate_schema(200) + + {:ok, _, _} = User.follow(user, other_user) + + assert [ + %{ + "id" => ^id, + "pleroma" => %{ + "relationship" => %{ + "id" => ^id, + "following" => true, + "followed_by" => true + } + } + } + ] = + conn + |> get("/api/v1/accounts/#{user.id}/followers?with_relationships=true") + |> json_response_and_validate_schema(200) + end + test "getting followers, hide_followers", %{user: user, conn: conn} do other_user = insert(:user, hide_followers: true) {:ok, _user, _other_user} = User.follow(user, other_user) @@ -645,6 +738,24 @@ test "getting following", %{user: user, conn: conn} do assert id == to_string(other_user.id) end + test "following with relationship", %{conn: conn, user: user} do + other_user = insert(:user) + {:ok, user, other_user} = User.follow(user, other_user) + + conn = get(conn, "/api/v1/accounts/#{user.id}/following?with_relationships=true") + + id = other_user.id + + assert [ + %{ + "id" => ^id, + "pleroma" => %{ + "relationship" => %{"id" => ^id, "following" => true, "followed_by" => false} + } + } + ] = json_response_and_validate_schema(conn, 200) + end + test "getting following, hide_follows, other user requesting" do user = insert(:user, hide_follows: true) other_user = insert(:user) @@ -992,7 +1103,7 @@ test "registers and logs in without :account_activation_required / :account_appr assert %{"error" => "{\"email\":[\"Invalid email\"]}"} = json_response_and_validate_schema(conn, 400) - Pleroma.Config.put([User, :email_blacklist], []) + clear_config([User, :email_blacklist], []) conn = build_conn() @@ -1012,8 +1123,8 @@ test "registers and logs in without :account_activation_required / :account_appr user = Repo.preload(token_from_db, :user).user assert user - refute user.confirmation_pending - refute user.approval_pending + assert user.is_confirmed + assert user.is_approved end test "registers but does not log in with :account_activation_required", %{conn: conn} do @@ -1073,7 +1184,7 @@ test "registers but does not log in with :account_activation_required", %{conn: refute response["token_type"] user = Repo.get_by(User, email: "lain@example.org") - assert user.confirmation_pending + refute user.is_confirmed end test "registers but does not log in with :account_approval_required", %{conn: conn} do @@ -1135,7 +1246,7 @@ test "registers but does not log in with :account_approval_required", %{conn: co user = Repo.get_by(User, email: "lain@example.org") - assert user.approval_pending + refute user.is_approved assert user.registration_reason == "I'm a cool dude, bro" end @@ -1411,8 +1522,6 @@ test "creates an account and returns 200 if captcha is valid", %{conn: conn} do |> json_response_and_validate_schema(:ok) assert Token |> Repo.get_by(token: access_token) |> Repo.preload(:user) |> Map.get(:user) - - Cachex.del(:used_captcha_cache, token) end test "returns 400 if any captcha field is not provided", %{conn: conn} do @@ -1552,7 +1661,6 @@ test "getting a list of mutes" do result = conn - |> assign(:user, user) |> get("/api/v1/mutes") |> json_response_and_validate_schema(200) @@ -1560,7 +1668,6 @@ test "getting a list of mutes" do result = conn - |> assign(:user, user) |> get("/api/v1/mutes?limit=1") |> json_response_and_validate_schema(200) @@ -1568,7 +1675,6 @@ test "getting a list of mutes" do result = conn - |> assign(:user, user) |> get("/api/v1/mutes?since_id=#{id1}") |> json_response_and_validate_schema(200) @@ -1576,7 +1682,6 @@ test "getting a list of mutes" do result = conn - |> assign(:user, user) |> get("/api/v1/mutes?since_id=#{id1}&max_id=#{id3}") |> json_response_and_validate_schema(200) @@ -1584,13 +1689,45 @@ test "getting a list of mutes" do result = conn - |> assign(:user, user) |> get("/api/v1/mutes?since_id=#{id1}&limit=1") |> json_response_and_validate_schema(200) assert [%{"id" => ^id2}] = result end + test "list of mutes with with_relationships parameter" do + %{user: user, conn: conn} = oauth_access(["read:mutes"]) + %{id: id1} = other_user1 = insert(:user) + %{id: id2} = other_user2 = insert(:user) + %{id: id3} = other_user3 = insert(:user) + + {:ok, _, _} = User.follow(other_user1, user) + {:ok, _, _} = User.follow(other_user2, user) + {:ok, _, _} = User.follow(other_user3, user) + + {:ok, _} = User.mute(user, other_user1) + {:ok, _} = User.mute(user, other_user2) + {:ok, _} = User.mute(user, other_user3) + + assert [ + %{ + "id" => ^id1, + "pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}} + }, + %{ + "id" => ^id2, + "pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}} + }, + %{ + "id" => ^id3, + "pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}} + } + ] = + conn + |> get("/api/v1/mutes?with_relationships=true") + |> json_response_and_validate_schema(200) + end + test "getting a list of blocks" do %{user: user, conn: conn} = oauth_access(["read:blocks"]) %{id: id1} = other_user1 = insert(:user) diff --git a/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs index a0b8b126c..76d81b942 100644 --- a/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AppControllerTest do @@ -12,22 +12,26 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do import Pleroma.Factory test "apps/verify_credentials", %{conn: conn} do - token = insert(:oauth_token) + user_bound_token = insert(:oauth_token) + app_bound_token = insert(:oauth_token, user: nil) + refute app_bound_token.user - conn = - conn - |> put_req_header("authorization", "Bearer #{token.token}") - |> get("/api/v1/apps/verify_credentials") + for token <- [app_bound_token, user_bound_token] do + conn = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/v1/apps/verify_credentials") - app = Repo.preload(token, :app).app + app = Repo.preload(token, :app).app - expected = %{ - "name" => app.client_name, - "website" => app.website, - "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) - } + expected = %{ + "name" => app.client_name, + "website" => app.website, + "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) + } - assert expected == json_response_and_validate_schema(conn, 200) + assert expected == json_response_and_validate_schema(conn, 200) + end end test "creates an oauth app", %{conn: conn} do diff --git a/test/pleroma/web/mastodon_api/controllers/auth_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/auth_controller_test.exs index d7834c876..1872dfd59 100644 --- a/test/pleroma/web/mastodon_api/controllers/auth_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/auth_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AuthControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Config alias Pleroma.Repo @@ -136,7 +136,7 @@ test "it returns 204 when user is not local", %{conn: conn, user: user} do end test "it returns 204 when user is deactivated", %{conn: conn, user: user} do - {:ok, user} = Repo.update(Ecto.Changeset.change(user, deactivated: true, local: true)) + {:ok, user} = Repo.update(Ecto.Changeset.change(user, is_active: false, local: true)) conn = post(conn, "/auth/password?email=#{user.email}") assert empty_json_response(conn) diff --git a/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs index b00615ac9..3176f1296 100644 --- a/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Conversation.Participation alias Pleroma.User @@ -217,6 +217,32 @@ test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200) end + test "Removes a conversation", %{user: user_one, conn: conn} do + user_two = insert(:user) + token = insert(:oauth_token, user: user_one, scopes: ["read:statuses", "write:conversations"]) + + {:ok, _direct} = create_direct_message(user_one, [user_two]) + {:ok, _direct} = create_direct_message(user_one, [user_two]) + + assert [%{"id" => conv1_id}, %{"id" => conv2_id}] = + conn + |> assign(:token, token) + |> get("/api/v1/conversations") + |> json_response_and_validate_schema(200) + + assert %{} = + conn + |> assign(:token, token) + |> delete("/api/v1/conversations/#{conv1_id}") + |> json_response_and_validate_schema(200) + + assert [%{"id" => ^conv2_id}] = + conn + |> assign(:token, token) + |> get("/api/v1/conversations") + |> json_response_and_validate_schema(200) + end + defp create_direct_message(sender, recips) do hellos = recips diff --git a/test/pleroma/web/mastodon_api/controllers/custom_emoji_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/custom_emoji_controller_test.exs index ab0027f90..cbb1d54a6 100644 --- a/test/pleroma/web/mastodon_api/controllers/custom_emoji_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/custom_emoji_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.CustomEmojiControllerTest do diff --git a/test/pleroma/web/mastodon_api/controllers/domain_block_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/domain_block_controller_test.exs index 664654500..0c3a7c0cf 100644 --- a/test/pleroma/web/mastodon_api/controllers/domain_block_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/domain_block_controller_test.exs @@ -1,8 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do + # TODO: Should not need Cachex use Pleroma.Web.ConnCase alias Pleroma.User diff --git a/test/pleroma/web/mastodon_api/controllers/filter_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/filter_controller_test.exs index 0d426ec34..98ab9e717 100644 --- a/test/pleroma/web/mastodon_api/controllers/filter_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/filter_controller_test.exs @@ -1,152 +1,415 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true + use Oban.Testing, repo: Pleroma.Repo - alias Pleroma.Web.MastodonAPI.FilterView + import Pleroma.Factory - test "creating a filter" do - %{conn: conn} = oauth_access(["write:filters"]) + alias Pleroma.Filter + alias Pleroma.Repo + alias Pleroma.Workers.PurgeExpiredFilter - filter = %Pleroma.Filter{ - phrase: "knights", - context: ["home"] - } - - conn = + test "non authenticated creation request", %{conn: conn} do + response = conn |> put_req_header("content-type", "application/json") - |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context}) + |> post("/api/v1/filters", %{"phrase" => "knights", context: ["home"]}) + |> json_response(403) - assert response = json_response_and_validate_schema(conn, 200) - assert response["phrase"] == filter.phrase - assert response["context"] == filter.context - assert response["irreversible"] == false - assert response["id"] != nil - assert response["id"] != "" + assert response["error"] == "Invalid credentials." + end + + describe "creating" do + setup do: oauth_access(["write:filters"]) + + test "a common filter", %{conn: conn, user: user} do + params = %{ + phrase: "knights", + context: ["home"], + irreversible: true + } + + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/filters", params) + |> json_response_and_validate_schema(200) + + assert response["phrase"] == params.phrase + assert response["context"] == params.context + assert response["irreversible"] == true + assert response["id"] != nil + assert response["id"] != "" + assert response["expires_at"] == nil + + filter = Filter.get(response["id"], user) + assert filter.hide == true + end + + test "a filter with expires_in", %{conn: conn, user: user} do + in_seconds = 600 + + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/filters", %{ + "phrase" => "knights", + context: ["home"], + expires_in: in_seconds + }) + |> json_response_and_validate_schema(200) + + assert response["irreversible"] == false + + expires_at = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(in_seconds) + |> Pleroma.Web.CommonAPI.Utils.to_masto_date() + + assert response["expires_at"] == expires_at + + filter = Filter.get(response["id"], user) + + id = filter.id + + assert_enqueued( + worker: PurgeExpiredFilter, + args: %{filter_id: filter.id} + ) + + assert {:ok, %{id: ^id}} = + perform_job(PurgeExpiredFilter, %{ + filter_id: filter.id + }) + + assert Repo.aggregate(Filter, :count, :id) == 0 + end end test "fetching a list of filters" do %{user: user, conn: conn} = oauth_access(["read:filters"]) - query_one = %Pleroma.Filter{ - user_id: user.id, - filter_id: 1, - phrase: "knights", - context: ["home"] - } + %{filter_id: id1} = insert(:filter, user: user) + %{filter_id: id2} = insert(:filter, user: user) - query_two = %Pleroma.Filter{ - user_id: user.id, - filter_id: 2, - phrase: "who", - context: ["home"] - } + id1 = to_string(id1) + id2 = to_string(id2) - {:ok, filter_one} = Pleroma.Filter.create(query_one) - {:ok, filter_two} = Pleroma.Filter.create(query_two) + assert [%{"id" => ^id2}, %{"id" => ^id1}] = + conn + |> get("/api/v1/filters") + |> json_response_and_validate_schema(200) + end + + test "fetching a list of filters without token", %{conn: conn} do + insert(:filter) response = conn |> get("/api/v1/filters") - |> json_response_and_validate_schema(200) + |> json_response(403) - assert response == - render_json( - FilterView, - "index.json", - filters: [filter_two, filter_one] - ) + assert response["error"] == "Invalid credentials." end test "get a filter" do %{user: user, conn: conn} = oauth_access(["read:filters"]) # check whole_word false - query = %Pleroma.Filter{ - user_id: user.id, - filter_id: 2, - phrase: "knight", - context: ["home"], - whole_word: false - } + filter = insert(:filter, user: user, whole_word: false) - {:ok, filter} = Pleroma.Filter.create(query) + resp1 = + conn |> get("/api/v1/filters/#{filter.filter_id}") |> json_response_and_validate_schema(200) - conn = get(conn, "/api/v1/filters/#{filter.filter_id}") - - assert response = json_response_and_validate_schema(conn, 200) - assert response["whole_word"] == false + assert resp1["whole_word"] == false # check whole_word true - %{user: user, conn: conn} = oauth_access(["read:filters"]) + filter = insert(:filter, user: user, whole_word: true) - query = %Pleroma.Filter{ - user_id: user.id, - filter_id: 3, - phrase: "knight", - context: ["home"], - whole_word: true - } + resp2 = + conn |> get("/api/v1/filters/#{filter.filter_id}") |> json_response_and_validate_schema(200) - {:ok, filter} = Pleroma.Filter.create(query) - - conn = get(conn, "/api/v1/filters/#{filter.filter_id}") - - assert response = json_response_and_validate_schema(conn, 200) - assert response["whole_word"] == true + assert resp2["whole_word"] == true end - test "update a filter" do - %{user: user, conn: conn} = oauth_access(["write:filters"]) + test "get a filter not_found error" do + filter = insert(:filter) + %{conn: conn} = oauth_access(["read:filters"]) - query = %Pleroma.Filter{ - user_id: user.id, - filter_id: 2, - phrase: "knight", - context: ["home"], - hide: true, - whole_word: true - } + response = + conn |> get("/api/v1/filters/#{filter.filter_id}") |> json_response_and_validate_schema(404) - {:ok, _filter} = Pleroma.Filter.create(query) + assert response["error"] == "Record not found" + end - new = %Pleroma.Filter{ - phrase: "nii", - context: ["home"] - } + describe "updating a filter" do + setup do: oauth_access(["write:filters"]) - conn = + test "common" do + %{conn: conn, user: user} = oauth_access(["write:filters"]) + + filter = + insert(:filter, + user: user, + hide: true, + whole_word: true + ) + + params = %{ + phrase: "nii", + context: ["public"], + irreversible: false + } + + response = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/filters/#{filter.filter_id}", params) + |> json_response_and_validate_schema(200) + + assert response["phrase"] == params.phrase + assert response["context"] == params.context + assert response["irreversible"] == false + assert response["whole_word"] == true + end + + test "with adding expires_at", %{conn: conn, user: user} do + filter = insert(:filter, user: user) + in_seconds = 600 + + response = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/filters/#{filter.filter_id}", %{ + phrase: "nii", + context: ["public"], + expires_in: in_seconds, + irreversible: true + }) + |> json_response_and_validate_schema(200) + + assert response["irreversible"] == true + + assert response["expires_at"] == + NaiveDateTime.utc_now() + |> NaiveDateTime.add(in_seconds) + |> Pleroma.Web.CommonAPI.Utils.to_masto_date() + + filter = Filter.get(response["id"], user) + + id = filter.id + + assert_enqueued( + worker: PurgeExpiredFilter, + args: %{filter_id: id} + ) + + assert {:ok, %{id: ^id}} = + perform_job(PurgeExpiredFilter, %{ + filter_id: id + }) + + assert Repo.aggregate(Filter, :count, :id) == 0 + end + + test "with removing expires_at", %{conn: conn, user: user} do + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/filters", %{ + phrase: "cofe", + context: ["home"], + expires_in: 600 + }) + |> json_response_and_validate_schema(200) + + filter = Filter.get(response["id"], user) + + assert_enqueued( + worker: PurgeExpiredFilter, + args: %{filter_id: filter.id} + ) + + response = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/filters/#{filter.filter_id}", %{ + phrase: "nii", + context: ["public"], + expires_in: nil, + whole_word: true + }) + |> json_response_and_validate_schema(200) + + refute_enqueued( + worker: PurgeExpiredFilter, + args: %{filter_id: filter.id} + ) + + assert response["irreversible"] == false + assert response["whole_word"] == true + assert response["expires_at"] == nil + end + + test "expires_at is the same in create and update so job is in db", %{conn: conn, user: user} do + resp1 = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/filters", %{ + phrase: "cofe", + context: ["home"], + expires_in: 600 + }) + |> json_response_and_validate_schema(200) + + filter = Filter.get(resp1["id"], user) + + assert_enqueued( + worker: PurgeExpiredFilter, + args: %{filter_id: filter.id} + ) + + job = PurgeExpiredFilter.get_expiration(filter.id) + + resp2 = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/filters/#{filter.filter_id}", %{ + phrase: "nii", + context: ["public"] + }) + |> json_response_and_validate_schema(200) + + updated_filter = Filter.get(resp2["id"], user) + + assert_enqueued( + worker: PurgeExpiredFilter, + args: %{filter_id: updated_filter.id} + ) + + after_update = PurgeExpiredFilter.get_expiration(updated_filter.id) + + assert resp1["expires_at"] == resp2["expires_at"] + + assert job.scheduled_at == after_update.scheduled_at + end + + test "updating expires_at updates oban job too", %{conn: conn, user: user} do + resp1 = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/filters", %{ + phrase: "cofe", + context: ["home"], + expires_in: 600 + }) + |> json_response_and_validate_schema(200) + + filter = Filter.get(resp1["id"], user) + + assert_enqueued( + worker: PurgeExpiredFilter, + args: %{filter_id: filter.id} + ) + + job = PurgeExpiredFilter.get_expiration(filter.id) + + resp2 = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/filters/#{filter.filter_id}", %{ + phrase: "nii", + context: ["public"], + expires_in: 300 + }) + |> json_response_and_validate_schema(200) + + updated_filter = Filter.get(resp2["id"], user) + + assert_enqueued( + worker: PurgeExpiredFilter, + args: %{filter_id: updated_filter.id} + ) + + after_update = PurgeExpiredFilter.get_expiration(updated_filter.id) + + refute resp1["expires_at"] == resp2["expires_at"] + + refute job.scheduled_at == after_update.scheduled_at + end + end + + test "update filter without token", %{conn: conn} do + filter = insert(:filter) + + response = conn |> put_req_header("content-type", "application/json") - |> put("/api/v1/filters/#{query.filter_id}", %{ - phrase: new.phrase, - context: new.context + |> put("/api/v1/filters/#{filter.filter_id}", %{ + phrase: "nii", + context: ["public"] }) + |> json_response(403) - assert response = json_response_and_validate_schema(conn, 200) - assert response["phrase"] == new.phrase - assert response["context"] == new.context - assert response["irreversible"] == true - assert response["whole_word"] == true + assert response["error"] == "Invalid credentials." end - test "delete a filter" do - %{user: user, conn: conn} = oauth_access(["write:filters"]) + describe "delete a filter" do + setup do: oauth_access(["write:filters"]) - query = %Pleroma.Filter{ - user_id: user.id, - filter_id: 2, - phrase: "knight", - context: ["home"] - } + test "common", %{conn: conn, user: user} do + filter = insert(:filter, user: user) - {:ok, filter} = Pleroma.Filter.create(query) + assert conn + |> delete("/api/v1/filters/#{filter.filter_id}") + |> json_response_and_validate_schema(200) == %{} - conn = delete(conn, "/api/v1/filters/#{filter.filter_id}") + assert Repo.all(Filter) == [] + end - assert json_response_and_validate_schema(conn, 200) == %{} + test "with expires_at", %{conn: conn, user: user} do + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/filters", %{ + phrase: "cofe", + context: ["home"], + expires_in: 600 + }) + |> json_response_and_validate_schema(200) + + filter = Filter.get(response["id"], user) + + assert_enqueued( + worker: PurgeExpiredFilter, + args: %{filter_id: filter.id} + ) + + assert conn + |> delete("/api/v1/filters/#{filter.filter_id}") + |> json_response_and_validate_schema(200) == %{} + + refute_enqueued( + worker: PurgeExpiredFilter, + args: %{filter_id: filter.id} + ) + + assert Repo.all(Filter) == [] + assert Repo.all(Oban.Job) == [] + end + end + + test "delete a filter without token", %{conn: conn} do + filter = insert(:filter) + + response = + conn + |> delete("/api/v1/filters/#{filter.filter_id}") + |> json_response(403) + + assert response["error"] == "Invalid credentials." end end diff --git a/test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs index b977b41ae..069ffb3d6 100644 --- a/test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.User alias Pleroma.Web.CommonAPI diff --git a/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs index 605df6ed6..b99856659 100644 --- a/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs @@ -1,8 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do + # TODO: Should not need Cachex use Pleroma.Web.ConnCase alias Pleroma.User @@ -46,6 +47,7 @@ test "get instance information", %{conn: conn} do assert result["pleroma"]["metadata"]["federation"] assert result["pleroma"]["metadata"]["fields_limits"] assert result["pleroma"]["vapid_public_key"] + assert result["pleroma"]["stats"]["mau"] == 0 assert email == from_config_email assert thumbnail == from_config_thumbnail @@ -56,7 +58,7 @@ test "get instance stats", %{conn: conn} do user = insert(:user, %{local: true}) user2 = insert(:user, %{local: true}) - {:ok, _user2} = User.deactivate(user2, !user2.deactivated) + {:ok, _user2} = User.set_activation(user2, false) insert(:user, %{local: false, nickname: "u@peer1.com"}) insert(:user, %{local: false, nickname: "u@peer2.com"}) diff --git a/test/pleroma/web/mastodon_api/controllers/list_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/list_controller_test.exs index 091ec006c..28099837e 100644 --- a/test/pleroma/web/mastodon_api/controllers/list_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/list_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ListControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Repo @@ -55,30 +55,39 @@ test "listing a user's lists" do test "adding users to a list" do %{user: user, conn: conn} = oauth_access(["write:lists"]) other_user = insert(:user) + third_user = insert(:user) {:ok, list} = Pleroma.List.create("name", user) assert %{} == conn |> put_req_header("content-type", "application/json") - |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) + |> post("/api/v1/lists/#{list.id}/accounts", %{ + "account_ids" => [other_user.id, third_user.id] + }) |> json_response_and_validate_schema(:ok) %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) - assert following == [other_user.follower_address] + assert length(following) == 2 + assert other_user.follower_address in following + assert third_user.follower_address in following end test "removing users from a list, body params" do %{user: user, conn: conn} = oauth_access(["write:lists"]) other_user = insert(:user) third_user = insert(:user) + fourth_user = insert(:user) {:ok, list} = Pleroma.List.create("name", user) {:ok, list} = Pleroma.List.follow(list, other_user) {:ok, list} = Pleroma.List.follow(list, third_user) + {:ok, list} = Pleroma.List.follow(list, fourth_user) assert %{} == conn |> put_req_header("content-type", "application/json") - |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) + |> delete("/api/v1/lists/#{list.id}/accounts", %{ + "account_ids" => [other_user.id, fourth_user.id] + }) |> json_response_and_validate_schema(:ok) %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) diff --git a/test/pleroma/web/mastodon_api/controllers/marker_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/marker_controller_test.exs index 9f0481120..53aebe8e4 100644 --- a/test/pleroma/web/mastodon_api/controllers/marker_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/marker_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true import Pleroma.Factory diff --git a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs index d2bd57515..6c8f984d5 100644 --- a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do diff --git a/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs index 9ac8488f6..2615912a8 100644 --- a/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do @@ -555,24 +555,11 @@ test "see notifications after muting user with notifications and with_muted para assert length(json_response_and_validate_schema(conn, 200)) == 1 end - @tag capture_log: true test "see move notifications" do old_user = insert(:user) new_user = insert(:user, also_known_as: [old_user.ap_id]) %{user: follower, conn: conn} = oauth_access(["read:notifications"]) - old_user_url = old_user.ap_id - - body = - File.read!("test/fixtures/users_mock/localhost.json") - |> String.replace("{{nickname}}", old_user.nickname) - |> Jason.encode!() - - Tesla.Mock.mock(fn - %{method: :get, url: ^old_user_url} -> - %Tesla.Env{status: 200, body: body} - end) - User.follow(follower, old_user) Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) Pleroma.Tests.ObanHelpers.perform_all() diff --git a/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs index f41de6448..da0a631a9 100644 --- a/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.PollControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Object alias Pleroma.Web.CommonAPI @@ -20,7 +20,7 @@ test "returns poll entity for object id", %{user: user, conn: conn} do poll: %{options: ["what Mastodon't", "n't what Mastodoes"], expires_in: 20} }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) conn = get(conn, "/api/v1/polls/#{object.id}") @@ -39,7 +39,7 @@ test "does not expose polls for private statuses", %{conn: conn} do visibility: "private" }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) conn = get(conn, "/api/v1/polls/#{object.id}") @@ -47,6 +47,78 @@ test "does not expose polls for private statuses", %{conn: conn} do end end + test "own_votes" do + %{conn: conn} = oauth_access(["write:statuses", "read:statuses"]) + + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(other_user, %{ + status: "A very delicious sandwich", + poll: %{ + options: ["Lettuce", "Grilled Bacon", "Tomato"], + expires_in: 20, + multiple: true + } + }) + + object = Object.normalize(activity, fetch: false) + + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 2]}) + |> json_response_and_validate_schema(200) + + object = Object.get_by_id(object.id) + + assert [ + %{ + "name" => "Lettuce", + "replies" => %{"totalItems" => 1, "type" => "Collection"}, + "type" => "Note" + }, + %{ + "name" => "Grilled Bacon", + "replies" => %{"totalItems" => 0, "type" => "Collection"}, + "type" => "Note" + }, + %{ + "name" => "Tomato", + "replies" => %{"totalItems" => 1, "type" => "Collection"}, + "type" => "Note" + } + ] == object.data["anyOf"] + + assert %{"replies" => %{"totalItems" => 0}} = + Enum.find(object.data["anyOf"], fn %{"name" => name} -> name == "Grilled Bacon" end) + + Enum.each(["Lettuce", "Tomato"], fn title -> + %{"replies" => %{"totalItems" => total_items}} = + Enum.find(object.data["anyOf"], fn %{"name" => name} -> name == title end) + + assert total_items == 1 + end) + + assert %{ + "own_votes" => own_votes, + "voted" => true + } = + conn + |> get("/api/v1/polls/#{object.id}") + |> json_response_and_validate_schema(200) + + assert 0 in own_votes + assert 2 in own_votes + # for non authenticated user + response = + build_conn() + |> get("/api/v1/polls/#{object.id}") + |> json_response_and_validate_schema(200) + + refute Map.has_key?(response, "own_votes") + refute Map.has_key?(response, "voted") + end + describe "POST /api/v1/polls/:id/votes" do setup do: oauth_access(["write:statuses"]) @@ -63,14 +135,13 @@ test "votes are added to the poll", %{conn: conn} do } }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]}) + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]}) + |> json_response_and_validate_schema(200) - assert json_response_and_validate_schema(conn, 200) object = Object.get_by_id(object.id) assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} -> @@ -85,7 +156,7 @@ test "author can't vote", %{user: user, conn: conn} do poll: %{options: ["Yes", "No"], expires_in: 20} }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert conn |> put_req_header("content-type", "application/json") @@ -106,7 +177,7 @@ test "does not allow multiple choices on a single-choice question", %{conn: conn poll: %{options: ["half empty", "half full"], expires_in: 20} }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert conn |> put_req_header("content-type", "application/json") @@ -129,7 +200,7 @@ test "does not allow choice index to be greater than options count", %{conn: con poll: %{options: ["Yes", "No"], expires_in: 20} }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) conn = conn @@ -158,7 +229,7 @@ test "returns 404 when poll is private and not available for user", %{conn: conn visibility: "private" }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) conn = conn diff --git a/test/pleroma/web/mastodon_api/controllers/report_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/report_controller_test.exs index 6636cff96..fcfc4a48a 100644 --- a/test/pleroma/web/mastodon_api/controllers/report_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/report_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Web.CommonAPI diff --git a/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs index 1ff871c89..b28e3df56 100644 --- a/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do @@ -55,7 +55,7 @@ test "shows a scheduled activity" do end test "updates a scheduled activity" do - Pleroma.Config.put([ScheduledActivity, :enabled], true) + clear_config([ScheduledActivity, :enabled], true) %{user: user, conn: conn} = oauth_access(["write:statuses"]) scheduled_at = Timex.shift(NaiveDateTime.utc_now(), minutes: 60) @@ -103,7 +103,7 @@ test "updates a scheduled activity" do end test "deletes a scheduled activity" do - Pleroma.Config.put([ScheduledActivity, :enabled], true) + clear_config([ScheduledActivity, :enabled], true) %{user: user, conn: conn} = oauth_access(["write:statuses"]) scheduled_at = Timex.shift(NaiveDateTime.utc_now(), minutes: 60) diff --git a/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs index 1045ab265..1dd0fa3b8 100644 --- a/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do @@ -309,7 +309,7 @@ test "search doesn't show statuses that it shouldn't", %{conn: conn} do }) capture_log(fn -> - q = Object.normalize(activity).data["id"] + q = Object.normalize(activity, fetch: false).data["id"] results = conn diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs index 30d542dfa..99ad87d05 100644 --- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do @@ -7,7 +7,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do use Oban.Testing, repo: Pleroma.Repo alias Pleroma.Activity - alias Pleroma.Config alias Pleroma.Conversation.Participation alias Pleroma.Object alias Pleroma.Repo @@ -29,7 +28,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do setup do: oauth_access(["write:statuses"]) test "posting a status does not increment reblog_count when relaying", %{conn: conn} do - Config.put([:instance, :federating], true) + clear_config([:instance, :federating], true) Config.get([:instance, :allow_relay], true) response = @@ -67,10 +66,6 @@ test "posting a status", %{conn: conn} do "sensitive" => "0" }) - {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key) - # Six hours - assert ttl > :timer.seconds(6 * 60 * 60 - 1) - assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} = json_response_and_validate_schema(conn_one, 200) @@ -155,8 +150,8 @@ test "it fails to create a status if `expires_in` is less or equal than an hour" end test "Get MRF reason when posting a status is rejected by one", %{conn: conn} do - Config.put([:mrf_keyword, :reject], ["GNO"]) - Config.put([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy]) + clear_config([:mrf_keyword, :reject], ["GNO"]) + clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy]) assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} = conn @@ -268,6 +263,7 @@ test "posting a fake status", %{conn: conn} do fake_conn = conn + |> assign(:user, refresh_record(conn.assigns.user)) |> put_req_header("content-type", "application/json") |> post("/api/v1/statuses", %{ "status" => @@ -361,6 +357,69 @@ test "posting a direct status", %{conn: conn} do assert activity.data["to"] == [user2.ap_id] assert activity.data["cc"] == [] end + + test "discloses application metadata when enabled" do + user = insert(:user, disclose_client: true) + %{user: _user, token: token, conn: conn} = oauth_access(["write:statuses"], user: user) + + %Pleroma.Web.OAuth.Token{ + app: %Pleroma.Web.OAuth.App{ + client_name: app_name, + website: app_website + } + } = token + + result = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ + "status" => "cofe is my copilot" + }) + + assert %{ + "content" => "cofe is my copilot" + } = json_response_and_validate_schema(result, 200) + + activity = result.assigns.activity.id + + result = + conn + |> get("api/v1/statuses/#{activity}") + + assert %{ + "content" => "cofe is my copilot", + "application" => %{ + "name" => ^app_name, + "website" => ^app_website + } + } = json_response_and_validate_schema(result, 200) + end + + test "hides application metadata when disabled" do + user = insert(:user, disclose_client: false) + %{user: _user, token: _token, conn: conn} = oauth_access(["write:statuses"], user: user) + + result = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ + "status" => "club mate is my wingman" + }) + + assert %{"content" => "club mate is my wingman"} = + json_response_and_validate_schema(result, 200) + + activity = result.assigns.activity.id + + result = + conn + |> get("api/v1/statuses/#{activity}") + + assert %{ + "content" => "club mate is my wingman", + "application" => nil + } = json_response_and_validate_schema(result, 200) + end end describe "posting scheduled statuses" do @@ -387,6 +446,31 @@ test "creates a scheduled activity", %{conn: conn} do assert [] == Repo.all(Activity) end + test "with expiration" do + %{conn: conn} = oauth_access(["write:statuses", "read:statuses"]) + + scheduled_at = + NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(6), :millisecond) + |> NaiveDateTime.to_iso8601() + |> Kernel.<>("Z") + + assert %{"id" => status_id, "params" => %{"expires_in" => 300}} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ + "status" => "scheduled", + "scheduled_at" => scheduled_at, + "expires_in" => 300 + }) + |> json_response_and_validate_schema(200) + + assert %{"id" => ^status_id, "params" => %{"expires_in" => 300}} = + conn + |> put_req_header("content-type", "application/json") + |> get("/api/v1/scheduled_statuses/#{status_id}") + |> json_response_and_validate_schema(200) + end + test "ignores nil values", %{conn: conn} do conn = conn @@ -520,7 +604,7 @@ test "posting a poll", %{conn: conn} do end) assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430 - refute response["poll"]["expred"] + assert response["poll"]["expired"] == false question = Object.get_by_id(response["poll"]["id"]) @@ -596,6 +680,44 @@ test "maximum date limit is enforced", %{conn: conn} do %{"error" => error} = json_response_and_validate_schema(conn, 422) assert error == "Expiration date is too far in the future" end + + test "scheduled poll", %{conn: conn} do + clear_config([ScheduledActivity, :enabled], true) + + scheduled_at = + NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(6), :millisecond) + |> NaiveDateTime.to_iso8601() + |> Kernel.<>("Z") + + %{"id" => scheduled_id} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses", %{ + "status" => "very cool poll", + "poll" => %{ + "options" => ~w(a b c), + "expires_in" => 420 + }, + "scheduled_at" => scheduled_at + }) + |> json_response_and_validate_schema(200) + + assert {:ok, %{id: activity_id}} = + perform_job(Pleroma.Workers.ScheduledActivityWorker, %{ + activity_id: scheduled_id + }) + + assert Repo.all(Oban.Job) == [] + + object = + Activity + |> Repo.get(activity_id) + |> Object.normalize() + + assert object.data["content"] == "very cool poll" + assert object.data["type"] == "Question" + assert length(object.data["oneOf"]) == 3 + end end test "get a status" do @@ -804,7 +926,7 @@ test "if user is authenticated", %{local: local, remote: remote} do test "when you created it" do %{user: author, conn: conn} = oauth_access(["write:statuses"]) activity = insert(:note_activity, user: author) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) content = object.data["content"] source = object.data["source"] @@ -958,6 +1080,23 @@ test "reblogged status for another user" do assert to_string(activity.id) == id end + + test "author can reblog own private status", %{conn: conn, user: user} do + {:ok, activity} = CommonAPI.post(user, %{status: "cofe", visibility: "private"}) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity.id}/reblog") + + assert %{ + "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}, + "reblogged" => true, + "visibility" => "private" + } = json_response_and_validate_schema(conn, 200) + + assert to_string(activity.id) == id + end end describe "unreblogging" do @@ -1070,20 +1209,27 @@ test "returns 404 error for a wrong id", %{conn: conn} do setup do: clear_config([:instance, :max_pinned_statuses], 1) test "pin status", %{conn: conn, user: user, activity: activity} do - id_str = to_string(activity.id) + id = activity.id - assert %{"id" => ^id_str, "pinned" => true} = + assert %{"id" => ^id, "pinned" => true} = conn |> put_req_header("content-type", "application/json") |> post("/api/v1/statuses/#{activity.id}/pin") |> json_response_and_validate_schema(200) - assert [%{"id" => ^id_str, "pinned" => true}] = + assert [%{"id" => ^id, "pinned" => true}] = conn |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") |> json_response_and_validate_schema(200) end + test "non authenticated user", %{activity: activity} do + assert build_conn() + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity.id}/pin") + |> json_response(403) == %{"error" => "Invalid credentials."} + end + test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do {:ok, dm} = CommonAPI.post(user, %{status: "test", visibility: "direct"}) @@ -1092,7 +1238,18 @@ test "/pin: returns 400 error when activity is not public", %{conn: conn, user: |> put_req_header("content-type", "application/json") |> post("/api/v1/statuses/#{dm.id}/pin") - assert json_response_and_validate_schema(conn, 400) == %{"error" => "Could not pin"} + assert json_response_and_validate_schema(conn, 422) == %{ + "error" => "Non-public status cannot be pinned" + } + end + + test "pin by another user", %{activity: activity} do + %{conn: conn} = oauth_access(["write:accounts"]) + + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/#{activity.id}/pin") + |> json_response(422) == %{"error" => "Someone else's status cannot be pinned"} end test "unpin status", %{conn: conn, user: user, activity: activity} do @@ -1113,13 +1270,11 @@ test "unpin status", %{conn: conn, user: user, activity: activity} do |> json_response_and_validate_schema(200) end - test "/unpin: returns 400 error when activity is not exist", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/statuses/1/unpin") - - assert json_response_and_validate_schema(conn, 400) == %{"error" => "Could not unpin"} + test "/unpin: returns 404 error when activity doesn't exist", %{conn: conn} do + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/statuses/1/unpin") + |> json_response_and_validate_schema(404) == %{"error" => "Record not found"} end test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do @@ -1191,7 +1346,7 @@ test "on pin removes deletion job, on unpin reschedule deletion" do describe "cards" do setup do - Config.put([:rich_media, :enabled], true) + clear_config([:rich_media, :enabled], true) oauth_access(["read:statuses"]) end @@ -1378,7 +1533,9 @@ test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{c activity = Activity.get_by_id_with_object(id) - assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"] + assert Object.normalize(activity, fetch: false).data["inReplyTo"] == + Object.normalize(replied_to, fetch: false).data["id"] + assert Activity.get_in_reply_to_activity(activity).id == replied_to.id # Reblog from the third user diff --git a/test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs index 5ef39bdb2..5a3f93d2d 100644 --- a/test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true import Pleroma.Factory diff --git a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs index 7f08e187c..168966fc9 100644 --- a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true setup do: oauth_access(["read"]) diff --git a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs index 655e35ac6..cc409451c 100644 --- a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do @@ -90,6 +90,65 @@ test "muted emotions", %{user: user, conn: conn} do } ] = result end + + test "filtering", %{conn: conn, user: user} do + local_user = insert(:user) + {:ok, user, local_user} = User.follow(user, local_user) + {:ok, local_activity} = CommonAPI.post(local_user, %{status: "Status"}) + with_media = create_with_media_activity(local_user) + + remote_user = insert(:user, local: false) + {:ok, _user, remote_user} = User.follow(user, remote_user) + remote_activity = create_remote_activity(remote_user) + + without_filter_ids = + conn + |> get("/api/v1/timelines/home") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + assert local_activity.id in without_filter_ids + assert remote_activity.id in without_filter_ids + assert with_media.id in without_filter_ids + + only_local_ids = + conn + |> get("/api/v1/timelines/home?local=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + assert local_activity.id in only_local_ids + refute remote_activity.id in only_local_ids + assert with_media.id in only_local_ids + + only_local_media_ids = + conn + |> get("/api/v1/timelines/home?local=true&only_media=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + refute local_activity.id in only_local_media_ids + refute remote_activity.id in only_local_media_ids + assert with_media.id in only_local_media_ids + + remote_ids = + conn + |> get("/api/v1/timelines/home?remote=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + refute local_activity.id in remote_ids + assert remote_activity.id in remote_ids + refute with_media.id in remote_ids + + assert conn + |> get("/api/v1/timelines/home?remote=true&only_media=true") + |> json_response_and_validate_schema(200) == [] + + assert conn + |> get("/api/v1/timelines/home?remote=true&local=true") + |> json_response_and_validate_schema(200) == [] + end end describe "public" do @@ -98,27 +157,80 @@ test "the public timeline", %{conn: conn} do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "test"}) + with_media = create_with_media_activity(user) - _activity = insert(:note_activity, local: false) + remote = insert(:note_activity, local: false) - conn = get(conn, "/api/v1/timelines/public?local=False") + assert conn + |> get("/api/v1/timelines/public?local=False") + |> json_response_and_validate_schema(:ok) + |> length == 3 - assert length(json_response_and_validate_schema(conn, :ok)) == 2 + local_ids = + conn + |> get("/api/v1/timelines/public?local=True") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) - conn = get(build_conn(), "/api/v1/timelines/public?local=True") + assert activity.id in local_ids + assert with_media.id in local_ids + refute remote.id in local_ids - assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) + local_ids = + conn + |> get("/api/v1/timelines/public?local=True") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) - conn = get(build_conn(), "/api/v1/timelines/public?local=1") + assert activity.id in local_ids + assert with_media.id in local_ids + refute remote.id in local_ids - assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) + local_ids = + conn + |> get("/api/v1/timelines/public?local=True&only_media=true") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + refute activity.id in local_ids + assert with_media.id in local_ids + refute remote.id in local_ids + + local_ids = + conn + |> get("/api/v1/timelines/public?local=1") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + assert activity.id in local_ids + assert with_media.id in local_ids + refute remote.id in local_ids + + remote_id = remote.id + + assert [%{"id" => ^remote_id}] = + conn + |> get("/api/v1/timelines/public?remote=true") + |> json_response_and_validate_schema(:ok) + + with_media_id = with_media.id + + assert [%{"id" => ^with_media_id}] = + conn + |> get("/api/v1/timelines/public?only_media=true") + |> json_response_and_validate_schema(:ok) + + assert conn + |> get("/api/v1/timelines/public?remote=true&only_media=true") + |> json_response_and_validate_schema(:ok) == [] # does not contain repeats {:ok, _} = CommonAPI.repeat(activity.id, user) - conn = get(build_conn(), "/api/v1/timelines/public?local=true") - - assert [_] = json_response_and_validate_schema(conn, :ok) + assert [_, _] = + conn + |> get("/api/v1/timelines/public?local=true") + |> json_response_and_validate_schema(:ok) end test "the public timeline includes only public statuses for an authenticated user" do @@ -544,6 +656,77 @@ test "muted emotions", %{user: user, conn: conn} do } ] = result end + + test "filtering", %{user: user, conn: conn} do + {:ok, list} = Pleroma.List.create("name", user) + + local_user = insert(:user) + {:ok, local_activity} = CommonAPI.post(local_user, %{status: "Marisa is stupid."}) + with_media = create_with_media_activity(local_user) + {:ok, list} = Pleroma.List.follow(list, local_user) + + remote_user = insert(:user, local: false) + remote_activity = create_remote_activity(remote_user) + {:ok, list} = Pleroma.List.follow(list, remote_user) + + all_ids = + conn + |> get("/api/v1/timelines/list/#{list.id}") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + assert local_activity.id in all_ids + assert with_media.id in all_ids + assert remote_activity.id in all_ids + + only_local_ids = + conn + |> get("/api/v1/timelines/list/#{list.id}?local=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + assert local_activity.id in only_local_ids + assert with_media.id in only_local_ids + refute remote_activity.id in only_local_ids + + only_local_media_ids = + conn + |> get("/api/v1/timelines/list/#{list.id}?local=true&only_media=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + refute local_activity.id in only_local_media_ids + assert with_media.id in only_local_media_ids + refute remote_activity.id in only_local_media_ids + + remote_ids = + conn + |> get("/api/v1/timelines/list/#{list.id}?remote=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + refute local_activity.id in remote_ids + refute with_media.id in remote_ids + assert remote_activity.id in remote_ids + + assert conn + |> get("/api/v1/timelines/list/#{list.id}?remote=true&only_media=true") + |> json_response_and_validate_schema(200) == [] + + only_media_ids = + conn + |> get("/api/v1/timelines/list/#{list.id}?only_media=true") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["id"]) + + refute local_activity.id in only_media_ids + assert with_media.id in only_media_ids + refute remote_activity.id in only_media_ids + + assert conn + |> get("/api/v1/timelines/list/#{list.id}?only_media=true&local=true&remote=true") + |> json_response_and_validate_schema(200) == [] + end end describe "hashtag" do @@ -554,19 +737,85 @@ test "hashtag timeline", %{conn: conn} do following = insert(:user) {:ok, activity} = CommonAPI.post(following, %{status: "test #2hu"}) + with_media = create_with_media_activity(following) - nconn = get(conn, "/api/v1/timelines/tag/2hu") + remote = insert(:user, local: false) + remote_activity = create_remote_activity(remote) - assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok) + all_ids = + conn + |> get("/api/v1/timelines/tag/2hu") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) - assert id == to_string(activity.id) + assert activity.id in all_ids + assert with_media.id in all_ids + assert remote_activity.id in all_ids # works for different capitalization too - nconn = get(conn, "/api/v1/timelines/tag/2HU") + all_ids = + conn + |> get("/api/v1/timelines/tag/2HU") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) - assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok) + assert activity.id in all_ids + assert with_media.id in all_ids + assert remote_activity.id in all_ids - assert id == to_string(activity.id) + local_ids = + conn + |> get("/api/v1/timelines/tag/2hu?local=true") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + assert activity.id in local_ids + assert with_media.id in local_ids + refute remote_activity.id in local_ids + + remote_ids = + conn + |> get("/api/v1/timelines/tag/2hu?remote=true") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + refute activity.id in remote_ids + refute with_media.id in remote_ids + assert remote_activity.id in remote_ids + + media_ids = + conn + |> get("/api/v1/timelines/tag/2hu?only_media=true") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + refute activity.id in media_ids + assert with_media.id in media_ids + refute remote_activity.id in media_ids + + media_local_ids = + conn + |> get("/api/v1/timelines/tag/2hu?only_media=true&local=true") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + refute activity.id in media_local_ids + assert with_media.id in media_local_ids + refute remote_activity.id in media_local_ids + + ids = + conn + |> get("/api/v1/timelines/tag/2hu?only_media=true&local=true&remote=true") + |> json_response_and_validate_schema(:ok) + |> Enum.map(& &1["id"]) + + refute activity.id in ids + refute with_media.id in ids + refute remote_activity.id in ids + + assert conn + |> get("/api/v1/timelines/tag/2hu?only_media=true&remote=true") + |> json_response_and_validate_schema(:ok) == [] end test "multi-hashtag timeline", %{conn: conn} do @@ -726,4 +975,37 @@ test "with `%{local: true, federated: false}`, forbids unauthenticated access to ensure_authenticated_access(base_uri) end end + + defp create_remote_activity(user) do + obj = + insert(:note, %{ + data: %{ + "to" => [ + "https://www.w3.org/ns/activitystreams#Public", + User.ap_followers(user) + ] + }, + user: user + }) + + insert(:note_activity, %{ + note: obj, + recipients: [ + "https://www.w3.org/ns/activitystreams#Public", + User.ap_followers(user) + ], + user: user, + local: false + }) + end + + defp create_with_media_activity(user) do + obj = insert(:attachment_note, user: user) + + insert(:note_activity, %{ + note: obj, + recipients: ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(user)], + user: user + }) + end end diff --git a/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs b/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs index b9cd050df..ea66c708f 100644 --- a/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs +++ b/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs @@ -1,11 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MastoFEControllerTest do use Pleroma.Web.ConnCase - alias Pleroma.Config alias Pleroma.User import Pleroma.Factory @@ -55,7 +54,7 @@ test "redirects not logged-in users to the login page on private instances", %{ conn: conn, path: path } do - Config.put([:instance, :public], false) + clear_config([:instance, :public], false) conn = get(conn, path) diff --git a/test/pleroma/web/mastodon_api/mastodon_api_controller_test.exs b/test/pleroma/web/mastodon_api/mastodon_api_controller_test.exs index bb4bc4396..c6332bd3e 100644 --- a/test/pleroma/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/pleroma/web/mastodon_api/mastodon_api_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true describe "empty_array/2 (stubs)" do test "GET /api/v1/accounts/:id/identity_proofs" do diff --git a/test/pleroma/web/mastodon_api/mastodon_api_test.exs b/test/pleroma/web/mastodon_api/mastodon_api_test.exs index 687fe5585..402bfd76f 100644 --- a/test/pleroma/web/mastodon_api/mastodon_api_test.exs +++ b/test/pleroma/web/mastodon_api/mastodon_api_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Notification alias Pleroma.ScheduledActivity @@ -16,7 +16,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do describe "follow/3" do test "returns error when followed user is deactivated" do follower = insert(:user) - user = insert(:user, local: true, deactivated: true) + user = insert(:user, local: true, is_active: false) assert {:error, _error} = MastodonAPI.follow(follower, user) end diff --git a/test/pleroma/web/mastodon_api/update_credentials_test.exs b/test/pleroma/web/mastodon_api/update_credentials_test.exs index ed1921c91..cfbe6cf0e 100644 --- a/test/pleroma/web/mastodon_api/update_credentials_test.exs +++ b/test/pleroma/web/mastodon_api/update_credentials_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do @@ -11,8 +11,6 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do import Mock import Pleroma.Factory - setup do: clear_config([:instance, :max_account_fields]) - describe "updating credentials" do setup do: oauth_access(["write:accounts"]) setup :request_content_type @@ -220,6 +218,25 @@ test "updates the user's name", %{conn: conn} do assert update_activity.data["object"]["name"] == "markorepairs" end + test "updates the user's AKAs", %{conn: conn} do + conn = + patch(conn, "/api/v1/accounts/update_credentials", %{ + "also_known_as" => ["https://mushroom.kingdom/users/mario"] + }) + + assert user_data = json_response_and_validate_schema(conn, 200) + assert user_data["pleroma"]["also_known_as"] == ["https://mushroom.kingdom/users/mario"] + end + + test "doesn't update non-url akas", %{conn: conn} do + conn = + patch(conn, "/api/v1/accounts/update_credentials", %{ + "also_known_as" => ["aReallyCoolGuy"] + }) + + assert json_response_and_validate_schema(conn, 403) + end + test "updates the user's avatar", %{user: user, conn: conn} do new_avatar = %Plug.Upload{ content_type: "image/jpeg", @@ -446,7 +463,7 @@ test "update fields when invalid request", %{conn: conn} do |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) |> json_response_and_validate_schema(403) - Pleroma.Config.put([:instance, :max_account_fields], 1) + clear_config([:instance, :max_account_fields], 1) fields = [ %{"name" => "foo", "value" => "bar"}, diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs index 8c77f14d4..5373a17c3 100644 --- a/test/pleroma/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -1,11 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AccountViewTest do use Pleroma.DataCase - alias Pleroma.Config alias Pleroma.User alias Pleroma.UserRelationship alias Pleroma.Web.CommonAPI @@ -35,7 +34,8 @@ test "Represent a user account" do "valid html. a
b
c
d
f '&<>\"", inserted_at: ~N[2017-08-15 15:47:06.597036], emoji: %{"karjalanpiirakka" => "/file.png"}, - raw_bio: "valid html. a\nb\nc\nd\nf '&<>\"" + raw_bio: "valid html. a\nb\nc\nd\nf '&<>\"", + also_known_as: ["https://shitposter.zone/users/shp"] }) expected = %{ @@ -73,11 +73,13 @@ test "Represent a user account" do }, fields: [] }, + fqn: "shp@shitposter.club", pleroma: %{ ap_id: user.ap_id, + also_known_as: ["https://shitposter.zone/users/shp"], background_image: "https://example.com/images/asuka_hospital.png", favicon: nil, - confirmation_pending: false, + is_confirmed: true, tags: [], is_admin: false, is_moderator: false, @@ -171,11 +173,13 @@ test "Represent a Service(bot) account" do }, fields: [] }, + fqn: "shp@shitposter.club", pleroma: %{ ap_id: user.ap_id, + also_known_as: [], background_image: nil, favicon: nil, - confirmation_pending: false, + is_confirmed: true, tags: [], is_admin: false, is_moderator: false, @@ -208,7 +212,7 @@ test "Represent a Funkwhale channel" do test "Represent a deactivated user for an admin" do admin = insert(:user, is_admin: true) - deactivated_user = insert(:user, deactivated: true) + deactivated_user = insert(:user, is_active: false) represented = AccountView.render("show.json", %{user: deactivated_user, for: admin}) assert represented[:pleroma][:deactivated] == true end @@ -553,7 +557,7 @@ test "uses mediaproxy urls when it's enabled (regardless of media preview proxy ) with media_preview_enabled <- [false, true] do - Config.put([:media_preview_proxy, :enabled], media_preview_enabled) + clear_config([:media_preview_proxy, :enabled], media_preview_enabled) AccountView.render("show.json", %{user: user, skip_visibility_check: true}) |> Enum.all?(fn diff --git a/test/pleroma/web/mastodon_api/views/conversation_view_test.exs b/test/pleroma/web/mastodon_api/views/conversation_view_test.exs index 20c10ba3d..9639e95d2 100644 --- a/test/pleroma/web/mastodon_api/views/conversation_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/conversation_view_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ConversationViewTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Conversation.Participation alias Pleroma.Web.CommonAPI diff --git a/test/pleroma/web/mastodon_api/views/list_view_test.exs b/test/pleroma/web/mastodon_api/views/list_view_test.exs index ca99242cb..a62495ebb 100644 --- a/test/pleroma/web/mastodon_api/views/list_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/list_view_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ListViewTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Web.MastodonAPI.ListView diff --git a/test/pleroma/web/mastodon_api/views/marker_view_test.exs b/test/pleroma/web/mastodon_api/views/marker_view_test.exs index 48a0a6d33..8d8c16f6c 100644 --- a/test/pleroma/web/mastodon_api/views/marker_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/marker_view_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.MarkerViewTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.MastodonAPI.MarkerView import Pleroma.Factory diff --git a/test/pleroma/web/mastodon_api/views/notification_view_test.exs b/test/pleroma/web/mastodon_api/views/notification_view_test.exs index 9de11a87e..496a688d1 100644 --- a/test/pleroma/web/mastodon_api/views/notification_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/notification_view_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do @@ -44,7 +44,7 @@ test "ChatMessage notification" do {:ok, [notification]} = Notification.create_notifications(activity) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) chat = Chat.get(recipient.id, user.ap_id) cm_ref = MessageReference.for_chat_and_object(chat, object) @@ -144,24 +144,11 @@ test "Follow notification" do refute Repo.one(Notification) end - @tag capture_log: true test "Move notification" do old_user = insert(:user) new_user = insert(:user, also_known_as: [old_user.ap_id]) follower = insert(:user) - old_user_url = old_user.ap_id - - body = - File.read!("test/fixtures/users_mock/localhost.json") - |> String.replace("{{nickname}}", old_user.nickname) - |> Jason.encode!() - - Tesla.Mock.mock(fn - %{method: :get, url: ^old_user_url} -> - %Tesla.Env{status: 200, body: body} - end) - User.follow(follower, old_user) Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) Pleroma.Tests.ObanHelpers.perform_all() diff --git a/test/pleroma/web/mastodon_api/views/poll_view_test.exs b/test/pleroma/web/mastodon_api/views/poll_view_test.exs index c655ca438..224b26cb9 100644 --- a/test/pleroma/web/mastodon_api/views/poll_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/poll_view_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.PollViewTest do @@ -29,7 +29,7 @@ test "renders a poll" do } }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) expected = %{ emojis: [], @@ -42,7 +42,6 @@ test "renders a poll" do %{title: "yes", votes_count: 0}, %{title: "why are you even asking?", votes_count: 0} ], - voted: false, votes_count: 0, voters_count: 0 } @@ -72,7 +71,7 @@ test "detects if it is multiple choice" do voter = insert(:user) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) {:ok, _votes, object} = CommonAPI.vote(voter, object, [0, 1]) @@ -98,7 +97,7 @@ test "detects emoji" do } }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert %{emojis: [%{shortcode: "blank"}]} = PollView.render("show.json", %{object: object}) end @@ -117,19 +116,21 @@ test "detects vote status" do } }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) {:ok, _, object} = CommonAPI.vote(other_user, object, [1, 2]) result = PollView.render("show.json", %{object: object, for: other_user}) assert result[:voted] == true + assert 1 in result[:own_votes] + assert 2 in result[:own_votes] assert Enum.at(result[:options], 1)[:votes_count] == 1 assert Enum.at(result[:options], 2)[:votes_count] == 1 end test "does not crash on polls with no end date" do - object = Object.normalize("https://skippers-bin.com/notes/7x9tmrp97i") + object = Object.normalize("https://skippers-bin.com/notes/7x9tmrp97i", fetch: true) result = PollView.render("show.json", %{object: object}) assert result[:expires_at] == nil @@ -153,7 +154,7 @@ test "doesn't strips HTML tags" do } }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert %{ options: [ diff --git a/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs b/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs index 04f73f5a0..e323f3a1f 100644 --- a/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ScheduledActivityViewTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.ScheduledActivity alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI @@ -58,7 +58,8 @@ test "A scheduled activity with a media attachment" do sensitive: true, spoiler_text: "spoiler", text: "hi", - visibility: "unlisted" + visibility: "unlisted", + expires_in: nil }, scheduled_at: Utils.to_masto_date(scheduled_activity.scheduled_at) } diff --git a/test/pleroma/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs index f2a7469ed..fbea25079 100644 --- a/test/pleroma/web/mastodon_api/views/status_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/status_view_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.StatusViewTest do @@ -61,7 +61,7 @@ test "works correctly with badly formatted emojis" do {:ok, activity} = CommonAPI.post(user, %{status: "yo"}) activity - |> Object.normalize(false) + |> Object.normalize(fetch: false) |> Object.update_data(%{"reactions" => %{"☕" => [user.ap_id], "x" => 1}}) activity = Activity.get_by_id(activity.id) @@ -160,7 +160,7 @@ test "returns a temporary ap_id based user for activities missing db users" do {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) Repo.delete(user) - Cachex.clear(:user_cache) + User.invalidate_cache(user) finger_url = "https://localhost/.well-known/webfinger?resource=acct:#{user.nickname}@localhost" @@ -194,7 +194,7 @@ test "tries to get a user by nickname if fetching by ap_id doesn't work" do |> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"}) |> Repo.update() - Cachex.clear(:user_cache) + User.invalidate_cache(user) result = StatusView.render("show.json", activity: activity) @@ -204,7 +204,7 @@ test "tries to get a user by nickname if fetching by ap_id doesn't work" do test "a note with null content" do note = insert(:note_activity) - note_object = Object.normalize(note) + note_object = Object.normalize(note, fetch: false) data = note_object.data @@ -223,7 +223,7 @@ test "a note with null content" do test "a note activity" do note = insert(:note_activity) - object_data = Object.normalize(note).data + object_data = Object.normalize(note, fetch: false).data user = User.get_cached_by_ap_id(note.data["actor"]) convo_id = Utils.context_to_conversation_id(object_data["context"]) @@ -262,14 +262,11 @@ test "a note activity" do mentions: [], tags: [ %{ - name: "#{object_data["tag"]}", - url: "/tag/#{object_data["tag"]}" + name: "#{hd(object_data["tag"])}", + url: "http://localhost:4001/tag/#{hd(object_data["tag"])}" } ], - application: %{ - name: "Web", - website: nil - }, + application: nil, language: nil, emojis: [ %{ @@ -289,7 +286,8 @@ test "a note activity" do direct_conversation_id: nil, thread_muted: false, emoji_reactions: [], - parent_visible: false + parent_visible: false, + pinned_at: nil } } @@ -585,9 +583,9 @@ test "it returns a a dictionary tags" do ] assert StatusView.build_tags(object_tags) == [ - %{name: "fediverse", url: "/tag/fediverse"}, - %{name: "mastodon", url: "/tag/mastodon"}, - %{name: "nextcloud", url: "/tag/nextcloud"} + %{name: "fediverse", url: "http://localhost:4001/tag/fediverse"}, + %{name: "mastodon", url: "http://localhost:4001/tag/mastodon"}, + %{name: "nextcloud", url: "http://localhost:4001/tag/nextcloud"} ] end end diff --git a/test/pleroma/web/mastodon_api/views/subscription_view_test.exs b/test/pleroma/web/mastodon_api/views/subscription_view_test.exs index 981524c0e..04b440389 100644 --- a/test/pleroma/web/mastodon_api/views/subscription_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/subscription_view_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.SubscriptionViewTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Web.MastodonAPI.SubscriptionView, as: View alias Pleroma.Web.Push diff --git a/test/pleroma/web/media_proxy/invalidation/http_test.exs b/test/pleroma/web/media_proxy/invalidation/http_test.exs index 13d081325..a15103c89 100644 --- a/test/pleroma/web/media_proxy/invalidation/http_test.exs +++ b/test/pleroma/web/media_proxy/invalidation/http_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do @@ -9,10 +9,6 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do import ExUnit.CaptureLog import Tesla.Mock - setup do - on_exit(fn -> Cachex.clear(:banned_urls_cache) end) - end - test "logs hasn't error message when request is valid" do mock(fn %{method: :purge, url: "http://example.com/media/example.jpg"} -> diff --git a/test/pleroma/web/media_proxy/invalidation/script_test.exs b/test/pleroma/web/media_proxy/invalidation/script_test.exs index 692cbb2df..e9629b72b 100644 --- a/test/pleroma/web/media_proxy/invalidation/script_test.exs +++ b/test/pleroma/web/media_proxy/invalidation/script_test.exs @@ -1,18 +1,14 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy.Invalidation.ScriptTest do - use ExUnit.Case + use ExUnit.Case, async: true alias Pleroma.Web.MediaProxy.Invalidation import ExUnit.CaptureLog - setup do - on_exit(fn -> Cachex.clear(:banned_urls_cache) end) - end - - test "it logger error when script not found" do + test "it logs error when script is not found" do assert capture_log(fn -> assert Invalidation.Script.purge( ["http://example.com/media/example.jpg"], @@ -27,4 +23,30 @@ test "it logger error when script not found" do ) == {:error, "\"not found script path\""} end) end + + describe "url formatting" do + setup do + urls = [ + "https://bikeshed.party/media/foo.png", + "http://safe.millennial.space/proxy/wheeeee.gif", + "https://lain.com/proxy/mediafile.mp4?foo&bar=true", + "http://localhost:4000/media/upload.jpeg" + ] + + [urls: urls] + end + + test "with invalid formatter", %{urls: urls} do + assert urls == Invalidation.Script.maybe_format_urls(urls, nil) + end + + test "with :htcacheclean formatter", %{urls: urls} do + assert [ + "https://bikeshed.party:443/media/foo.png?", + "http://safe.millennial.space:80/proxy/wheeeee.gif?", + "https://lain.com:443/proxy/mediafile.mp4?foo&bar=true", + "http://localhost:4000/media/upload.jpeg?" + ] == Invalidation.Script.maybe_format_urls(urls, :htcacheclean) + end + end end diff --git a/test/pleroma/web/media_proxy/invalidation_test.exs b/test/pleroma/web/media_proxy/invalidation_test.exs index aa1435ac0..c77b8c94a 100644 --- a/test/pleroma/web/media_proxy/invalidation_test.exs +++ b/test/pleroma/web/media_proxy/invalidation_test.exs @@ -1,12 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy.InvalidationTest do - use ExUnit.Case - use Pleroma.Tests.Helpers + use Pleroma.DataCase - alias Pleroma.Config alias Pleroma.Web.MediaProxy.Invalidation import ExUnit.CaptureLog @@ -15,17 +13,13 @@ defmodule Pleroma.Web.MediaProxy.InvalidationTest do setup do: clear_config([:media_proxy]) - setup do - on_exit(fn -> Cachex.clear(:banned_urls_cache) end) - end - describe "Invalidation.Http" do test "perform request to clear cache" do - Config.put([:media_proxy, :enabled], false) - Config.put([:media_proxy, :invalidation, :enabled], true) - Config.put([:media_proxy, :invalidation, :provider], Invalidation.Http) + clear_config([:media_proxy, :enabled], false) + clear_config([:media_proxy, :invalidation, :enabled], true) + clear_config([:media_proxy, :invalidation, :provider], Invalidation.Http) - Config.put([Invalidation.Http], method: :purge, headers: [{"x-refresh", 1}]) + clear_config([Invalidation.Http], method: :purge, headers: [{"x-refresh", 1}]) image_url = "http://example.com/media/example.jpg" Pleroma.Web.MediaProxy.put_in_banned_urls(image_url) @@ -48,10 +42,10 @@ test "perform request to clear cache" do describe "Invalidation.Script" do test "run script to clear cache" do - Config.put([:media_proxy, :enabled], false) - Config.put([:media_proxy, :invalidation, :enabled], true) - Config.put([:media_proxy, :invalidation, :provider], Invalidation.Script) - Config.put([Invalidation.Script], script_path: "purge-nginx") + clear_config([:media_proxy, :enabled], false) + clear_config([:media_proxy, :invalidation, :enabled], true) + clear_config([:media_proxy, :invalidation, :provider], Invalidation.Script) + clear_config([Invalidation.Script], script_path: "purge-nginx") image_url = "http://example.com/media/example.jpg" Pleroma.Web.MediaProxy.put_in_banned_urls(image_url) diff --git a/test/pleroma/web/media_proxy/media_proxy_controller_test.exs b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs index e9b584822..2a449e56d 100644 --- a/test/pleroma/web/media_proxy/media_proxy_controller_test.exs +++ b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do @@ -10,10 +10,6 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do alias Pleroma.Web.MediaProxy alias Plug.Conn - setup do - on_exit(fn -> Cachex.clear(:banned_urls_cache) end) - end - describe "Media Proxy" do setup do clear_config([:media_proxy, :enabled], true) @@ -37,7 +33,7 @@ test "it returns 404 when disabled", %{conn: conn} do end test "it returns 403 for invalid signature", %{conn: conn, url: url} do - Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000") + clear_config([Pleroma.Web.Endpoint, :secret_key_base], "000") %{path: path} = URI.parse(url) assert %Conn{ @@ -132,7 +128,7 @@ test "returns 404 when disabled", %{conn: conn} do end test "it returns 403 for invalid signature", %{conn: conn, url: url} do - Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000") + clear_config([Pleroma.Web.Endpoint, :secret_key_base], "000") %{path: path} = URI.parse(url) assert %Conn{ diff --git a/test/pleroma/web/media_proxy_test.exs b/test/pleroma/web/media_proxy_test.exs index 0e6df826c..b5ee6328d 100644 --- a/test/pleroma/web/media_proxy_test.exs +++ b/test/pleroma/web/media_proxy_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MediaProxyTest do @@ -11,8 +11,7 @@ defmodule Pleroma.Web.MediaProxyTest do alias Pleroma.Web.MediaProxy defp decode_result(encoded) do - [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") - {:ok, decoded} = MediaProxy.decode_url(sig, base64) + {:ok, decoded} = MediaProxy.decode_url(encoded) decoded end diff --git a/test/pleroma/web/metadata/player_view_test.exs b/test/pleroma/web/metadata/player_view_test.exs index e6c990242..58caf6efd 100644 --- a/test/pleroma/web/metadata/player_view_test.exs +++ b/test/pleroma/web/metadata/player_view_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.PlayerViewTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.Metadata.PlayerView diff --git a/test/pleroma/web/metadata/providers/feed_test.exs b/test/pleroma/web/metadata/providers/feed_test.exs index e6e5cc5ed..013d42498 100644 --- a/test/pleroma/web/metadata/providers/feed_test.exs +++ b/test/pleroma/web/metadata/providers/feed_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.FeedTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Web.Metadata.Providers.Feed diff --git a/test/pleroma/web/metadata/providers/open_graph_test.exs b/test/pleroma/web/metadata/providers/open_graph_test.exs index 218540e6c..fc44b3cbd 100644 --- a/test/pleroma/web/metadata/providers/open_graph_test.exs +++ b/test/pleroma/web/metadata/providers/open_graph_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do @@ -66,7 +66,7 @@ test "it renders all supported types of attachments and skips unknown types" do end test "it does not render attachments if post is nsfw" do - Pleroma.Config.put([Pleroma.Web.Metadata, :unfurl_nsfw], false) + clear_config([Pleroma.Web.Metadata, :unfurl_nsfw], false) user = insert(:user, avatar: %{"url" => [%{"href" => "https://pleroma.gov/tenshi.png"}]}) note = diff --git a/test/pleroma/web/metadata/providers/rel_me_test.exs b/test/pleroma/web/metadata/providers/rel_me_test.exs index 2293d6e13..0db6e7d22 100644 --- a/test/pleroma/web/metadata/providers/rel_me_test.exs +++ b/test/pleroma/web/metadata/providers/rel_me_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.RelMeTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Web.Metadata.Providers.RelMe diff --git a/test/pleroma/web/metadata/providers/restrict_indexing_test.exs b/test/pleroma/web/metadata/providers/restrict_indexing_test.exs index 52399fdc8..aa253e5e2 100644 --- a/test/pleroma/web/metadata/providers/restrict_indexing_test.exs +++ b/test/pleroma/web/metadata/providers/restrict_indexing_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.RestrictIndexingTest do diff --git a/test/pleroma/web/metadata/providers/twitter_card_test.exs b/test/pleroma/web/metadata/providers/twitter_card_test.exs index 10931b5ba..a35e44356 100644 --- a/test/pleroma/web/metadata/providers/twitter_card_test.exs +++ b/test/pleroma/web/metadata/providers/twitter_card_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do @@ -54,7 +54,7 @@ test "it uses summary twittercard if post has no attachment" do end test "it renders avatar not attachment if post is nsfw and unfurl_nsfw is disabled" do - Pleroma.Config.put([Pleroma.Web.Metadata, :unfurl_nsfw], false) + clear_config([Pleroma.Web.Metadata, :unfurl_nsfw], false) user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) diff --git a/test/pleroma/web/metadata/utils_test.exs b/test/pleroma/web/metadata/utils_test.exs index 8183256d8..074bd2e2f 100644 --- a/test/pleroma/web/metadata/utils_test.exs +++ b/test/pleroma/web/metadata/utils_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.UtilsTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Web.Metadata.Utils diff --git a/test/pleroma/web/mongoose_im_controller_test.exs b/test/pleroma/web/mongoose_im_controller_test.exs index e3a8aa3d8..43c4dfa33 100644 --- a/test/pleroma/web/mongoose_im_controller_test.exs +++ b/test/pleroma/web/mongoose_im_controller_test.exs @@ -1,15 +1,15 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MongooseIMControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true import Pleroma.Factory test "/user_exists", %{conn: conn} do _user = insert(:user, nickname: "lain") _remote_user = insert(:user, nickname: "alice", local: false) - _deactivated_user = insert(:user, nickname: "konata", deactivated: true) + _deactivated_user = insert(:user, nickname: "konata", is_active: false) res = conn @@ -41,13 +41,13 @@ test "/user_exists", %{conn: conn} do end test "/check_password", %{conn: conn} do - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("cool")) + user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("cool")) _deactivated_user = insert(:user, nickname: "konata", - deactivated: true, - password_hash: Pbkdf2.hash_pwd_salt("cool") + is_active: false, + password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("cool") ) res = diff --git a/test/pleroma/web/node_info_test.exs b/test/pleroma/web/node_info_test.exs index 06b33607f..ee6fdaae8 100644 --- a/test/pleroma/web/node_info_test.exs +++ b/test/pleroma/web/node_info_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.NodeInfoTest do @@ -7,8 +7,6 @@ defmodule Pleroma.Web.NodeInfoTest do import Pleroma.Factory - alias Pleroma.Config - setup do: clear_config([:mrf_simple]) setup do: clear_config(:instance) @@ -93,7 +91,7 @@ test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do assert "safe_dm_mentions" in response["metadata"]["features"] - Config.put([:instance, :safe_dm_mentions], false) + clear_config([:instance, :safe_dm_mentions], false) response = conn @@ -107,7 +105,7 @@ test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do setup do: clear_config([:instance, :federating]) test "it shows if federation is enabled/disabled", %{conn: conn} do - Config.put([:instance, :federating], true) + clear_config([:instance, :federating], true) response = conn @@ -116,7 +114,7 @@ test "it shows if federation is enabled/disabled", %{conn: conn} do assert response["metadata"]["federation"]["enabled"] == true - Config.put([:instance, :federating], false) + clear_config([:instance, :federating], false) response = conn diff --git a/test/pleroma/web/o_auth/app_test.exs b/test/pleroma/web/o_auth/app_test.exs index 993a490e0..fc2f0d940 100644 --- a/test/pleroma/web/o_auth/app_test.exs +++ b/test/pleroma/web/o_auth/app_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.AppTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.OAuth.App import Pleroma.Factory diff --git a/test/pleroma/web/o_auth/authorization_test.exs b/test/pleroma/web/o_auth/authorization_test.exs index d74b26cf8..fc1c04c4c 100644 --- a/test/pleroma/web/o_auth/authorization_test.exs +++ b/test/pleroma/web/o_auth/authorization_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.AuthorizationTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Authorization import Pleroma.Factory diff --git a/test/pleroma/web/o_auth/ldap_authorization_test.exs b/test/pleroma/web/o_auth/ldap_authorization_test.exs index 63b1c0eb8..61b9ce6b7 100644 --- a/test/pleroma/web/o_auth/ldap_authorization_test.exs +++ b/test/pleroma/web/o_auth/ldap_authorization_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do @@ -18,7 +18,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do @tag @skip test "authorizes the existing user using LDAP credentials" do password = "testpassword" - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) + user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password)) app = insert(:oauth_app, scopes: ["read", "write"]) host = Pleroma.Config.get([:ldap, :host]) |> to_charlist @@ -101,7 +101,7 @@ test "creates a new user after successful LDAP authorization" do @tag @skip test "disallow authorization for wrong LDAP credentials" do password = "testpassword" - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) + user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password)) app = insert(:oauth_app, scopes: ["read", "write"]) host = Pleroma.Config.get([:ldap, :host]) |> to_charlist diff --git a/test/pleroma/web/o_auth/mfa_controller_test.exs b/test/pleroma/web/o_auth/mfa_controller_test.exs index 6ecd0f6c9..17bbde85b 100644 --- a/test/pleroma/web/o_auth/mfa_controller_test.exs +++ b/test/pleroma/web/o_auth/mfa_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2018 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.MFAControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true import Pleroma.Factory alias Pleroma.MFA @@ -20,7 +20,7 @@ defmodule Pleroma.Web.OAuth.MFAControllerTest do insert(:user, multi_factor_authentication_settings: %MFA.Settings{ enabled: true, - backup_codes: [Pbkdf2.hash_pwd_salt("test-code")], + backup_codes: [Pleroma.Password.Pbkdf2.hash_pwd_salt("test-code")], totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} } ) @@ -246,7 +246,7 @@ test "returns access token with valid code", %{conn: conn, app: app} do hashed_codes = backup_codes - |> Enum.map(&Pbkdf2.hash_pwd_salt(&1)) + |> Enum.map(&Pleroma.Password.Pbkdf2.hash_pwd_salt(&1)) user = insert(:user, diff --git a/test/pleroma/web/o_auth/o_auth_controller_test.exs b/test/pleroma/web/o_auth/o_auth_controller_test.exs index ac22856ea..0fdd5b8e9 100644 --- a/test/pleroma/web/o_auth/o_auth_controller_test.exs +++ b/test/pleroma/web/o_auth/o_auth_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.OAuthControllerTest do @@ -316,7 +316,7 @@ test "with valid params, POST /oauth/register?op=connect redirects to `redirect_ app: app, conn: conn } do - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("testpassword")) + user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("testpassword")) registration = insert(:registration, user: nil) redirect_uri = OAuthController.default_redirect_uri(app) @@ -347,7 +347,7 @@ test "with unlisted `redirect_uri`, POST /oauth/register?op=connect results in H app: app, conn: conn } do - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("testpassword")) + user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("testpassword")) registration = insert(:registration, user: nil) unlisted_redirect_uri = "http://cross-site-request.com" @@ -790,7 +790,7 @@ test "issues a token for an all-body request" do test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do password = "testpassword" - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) + user = insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password)) app = insert(:oauth_app, scopes: ["read", "write"]) @@ -805,10 +805,12 @@ test "issues a token for `password` grant_type with valid credentials, with full "client_secret" => app.client_secret }) - assert %{"access_token" => token} = json_response(conn, 200) + assert %{"id" => id, "access_token" => access_token} = json_response(conn, 200) - token = Repo.get_by(Token, token: token) + token = Repo.get_by(Token, token: access_token) assert token + assert token.id == id + assert token.token == access_token assert token.scopes == app.scopes end @@ -818,7 +820,7 @@ test "issues a mfa token for `password` grant_type, when MFA enabled" do user = insert(:user, - password_hash: Pbkdf2.hash_pwd_salt(password), + password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password), multi_factor_authentication_settings: %MFA.Settings{ enabled: true, totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} @@ -923,12 +925,12 @@ test "rejects token exchange with invalid client credentials" do end test "rejects token exchange for valid credentials belonging to unconfirmed user and confirmation is required" do - Pleroma.Config.put([:instance, :account_activation_required], true) + clear_config([:instance, :account_activation_required], true) password = "testpassword" {:ok, user} = - insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) - |> User.confirmation_changeset(need_confirmation: true) + insert(:user, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password)) + |> User.confirmation_changeset(set_confirmation: false) |> User.update_and_set_cache() refute Pleroma.User.account_status(user) == :active @@ -955,8 +957,8 @@ test "rejects token exchange for valid credentials belonging to deactivated user user = insert(:user, - password_hash: Pbkdf2.hash_pwd_salt(password), - deactivated: true + password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password), + is_active: false ) app = insert(:oauth_app) @@ -983,7 +985,7 @@ test "rejects token exchange for user with password_reset_pending set to true" d user = insert(:user, - password_hash: Pbkdf2.hash_pwd_salt(password), + password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password), password_reset_pending: true ) @@ -1007,13 +1009,13 @@ test "rejects token exchange for user with password_reset_pending set to true" d end test "rejects token exchange for user with confirmation_pending set to true" do - Pleroma.Config.put([:instance, :account_activation_required], true) + clear_config([:instance, :account_activation_required], true) password = "testpassword" user = insert(:user, - password_hash: Pbkdf2.hash_pwd_salt(password), - confirmation_pending: true + password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password), + is_confirmed: false ) app = insert(:oauth_app, scopes: ["read", "write"]) @@ -1038,7 +1040,11 @@ test "rejects token exchange for user with confirmation_pending set to true" do test "rejects token exchange for valid credentials belonging to an unapproved user" do password = "testpassword" - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password), approval_pending: true) + user = + insert(:user, + password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password), + is_approved: false + ) refute Pleroma.User.account_status(user) == :active @@ -1082,7 +1088,7 @@ test "rejects an invalid authorization code" do setup do: clear_config([:oauth2, :issue_new_refresh_token]) test "issues a new access token with keep fresh token" do - Pleroma.Config.put([:oauth2, :issue_new_refresh_token], true) + clear_config([:oauth2, :issue_new_refresh_token], true) user = insert(:user) app = insert(:oauth_app, scopes: ["read", "write"]) @@ -1121,7 +1127,7 @@ test "issues a new access token with keep fresh token" do end test "issues a new access token with new fresh token" do - Pleroma.Config.put([:oauth2, :issue_new_refresh_token], false) + clear_config([:oauth2, :issue_new_refresh_token], false) user = insert(:user) app = insert(:oauth_app, scopes: ["read", "write"]) diff --git a/test/pleroma/web/o_auth/token/utils_test.exs b/test/pleroma/web/o_auth/token/utils_test.exs index a610d92f8..d2e7a0904 100644 --- a/test/pleroma/web/o_auth/token/utils_test.exs +++ b/test/pleroma/web/o_auth/token/utils_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.Token.UtilsTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.OAuth.Token.Utils import Pleroma.Factory diff --git a/test/pleroma/web/o_auth/token_test.exs b/test/pleroma/web/o_auth/token_test.exs index c88b9cc98..8c0858ebc 100644 --- a/test/pleroma/web/o_auth/token_test.exs +++ b/test/pleroma/web/o_auth/token_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth.TokenTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Repo alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Authorization diff --git a/test/pleroma/web/o_status/o_status_controller_test.exs b/test/pleroma/web/o_status/o_status_controller_test.exs index 65b2c22db..2038f4ddd 100644 --- a/test/pleroma/web/o_status/o_status_controller_test.exs +++ b/test/pleroma/web/o_status/o_status_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OStatus.OStatusControllerTest do @@ -72,7 +72,7 @@ test "redirects to /notice/:id for html format for activity", %{ test "redirects to /notice/id for html format", %{conn: conn} do note_activity = insert(:note_activity) - object = Object.normalize(note_activity) + object = Object.normalize(note_activity, fetch: false) [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"])) url = "/objects/#{uuid}" @@ -82,7 +82,7 @@ test "redirects to /notice/id for html format", %{conn: conn} do test "404s on private objects", %{conn: conn} do note_activity = insert(:direct_note_activity) - object = Object.normalize(note_activity) + object = Object.normalize(note_activity, fetch: false) [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"])) conn @@ -133,7 +133,7 @@ test "redirects to a proper object URL when json requested and the object is loc conn: conn } do note_activity = insert(:note_activity) - expected_redirect_url = Object.normalize(note_activity).data["id"] + expected_redirect_url = Object.normalize(note_activity, fetch: false).data["id"] redirect_url = conn @@ -144,13 +144,19 @@ test "redirects to a proper object URL when json requested and the object is loc assert redirect_url == expected_redirect_url end - test "returns a 404 on remote notice when json requested", %{conn: conn} do + test "redirects to a proper object URL when json requested and the object is remote", %{ + conn: conn + } do note_activity = insert(:note_activity, local: false) + expected_redirect_url = Object.normalize(note_activity, fetch: false).data["id"] - conn - |> put_req_header("accept", "application/activity+json") - |> get("/notice/#{note_activity.id}") - |> response(404) + redirect_url = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/notice/#{note_activity.id}") + |> redirected_to() + + assert redirect_url == expected_redirect_url end test "500s when actor not found", %{conn: conn} do @@ -230,7 +236,7 @@ test "does not require authentication on non-federating instances", %{ describe "GET /notice/:id/embed_player" do setup do note_activity = insert(:note_activity) - object = Pleroma.Object.normalize(note_activity) + object = Pleroma.Object.normalize(note_activity, fetch: false) object_data = Map.put(object.data, "attachment", [ @@ -287,7 +293,7 @@ test "404s when activity is direct message", %{conn: conn} do test "404s when attachment is empty", %{conn: conn} do note_activity = insert(:note_activity) - object = Pleroma.Object.normalize(note_activity) + object = Pleroma.Object.normalize(note_activity, fetch: false) object_data = Map.put(object.data, "attachment", []) object @@ -301,7 +307,7 @@ test "404s when attachment is empty", %{conn: conn} do test "404s when attachment isn't audio or video", %{conn: conn} do note_activity = insert(:note_activity) - object = Pleroma.Object.normalize(note_activity) + object = Pleroma.Object.normalize(note_activity, fetch: false) object_data = Map.put(object.data, "attachment", [ diff --git a/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs index 07909d48b..9f14c5577 100644 --- a/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do @@ -17,10 +17,10 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do setup do {:ok, user} = insert(:user) - |> User.confirmation_changeset(need_confirmation: true) + |> User.confirmation_changeset(set_confirmation: false) |> User.update_and_set_cache() - assert user.confirmation_pending + refute user.is_confirmed [user: user] end diff --git a/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs index f1941f6dd..3ee660a05 100644 --- a/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.BackupControllerTest do diff --git a/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs index a6c9d0c1b..99b0d43a7 100644 --- a/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do use Pleroma.Web.ConnCase @@ -22,7 +22,7 @@ test "it marks one message as read", %{conn: conn, user: user} do {:ok, create} = CommonAPI.post_chat_message(other_user, user, "sup") {:ok, _create} = CommonAPI.post_chat_message(other_user, user, "sup part 2") {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) - object = Object.normalize(create, false) + object = Object.normalize(create, fetch: false) cm_ref = MessageReference.for_chat_and_object(chat, object) assert cm_ref.unread == true @@ -52,7 +52,7 @@ test "given a `last_read_id`, it marks everything until then as read", %{ {:ok, create} = CommonAPI.post_chat_message(other_user, user, "sup") {:ok, _create} = CommonAPI.post_chat_message(other_user, user, "sup part 2") {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) - object = Object.normalize(create, false) + object = Object.normalize(create, fetch: false) cm_ref = MessageReference.for_chat_and_object(chat, object) assert cm_ref.unread == true @@ -158,7 +158,7 @@ test "it deletes a message from the chat", %{conn: conn, user: user} do {:ok, other_message} = CommonAPI.post_chat_message(recipient, user, "nico nico ni") - object = Object.normalize(message, false) + object = Object.normalize(message, fetch: false) chat = Chat.get(user.id, recipient.ap_id) @@ -176,7 +176,7 @@ test "it deletes a message from the chat", %{conn: conn, user: user} do assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id) # Deleting other people's messages just removes the reference - object = Object.normalize(other_message, false) + object = Object.normalize(other_message, fetch: false) cm_ref = MessageReference.for_chat_and_object(chat, object) result = @@ -211,12 +211,12 @@ test "it paginates", %{conn: conn, user: user} do assert String.match?( next, - ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$) + ~r(#{api_endpoint}.*/messages\?limit=\d+&max_id=.*; rel=\"next\"$) ) assert String.match?( prev, - ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&min_id=.*; rel=\"prev\"$) + ~r(#{api_endpoint}.*/messages\?limit=\d+&min_id=.*; rel=\"prev\"$) ) assert length(result) == 20 @@ -229,12 +229,12 @@ test "it paginates", %{conn: conn, user: user} do assert String.match?( next, - ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$) + ~r(#{api_endpoint}.*/messages\?limit=\d+&max_id=.*; rel=\"next\"$) ) assert String.match?( prev, - ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*&min_id=.*; rel=\"prev\"$) + ~r(#{api_endpoint}.*/messages\?limit=\d+&max_id=.*&min_id=.*; rel=\"prev\"$) ) assert length(result) == 10 @@ -304,139 +304,165 @@ test "it returns a chat", %{conn: conn, user: user} do end end - describe "GET /api/v1/pleroma/chats" do - setup do: oauth_access(["read:chats"]) + for tested_endpoint <- ["/api/v1/pleroma/chats", "/api/v2/pleroma/chats"] do + describe "GET #{tested_endpoint}" do + setup do: oauth_access(["read:chats"]) - test "it does not return chats with deleted users", %{conn: conn, user: user} do - recipient = insert(:user) - {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id) - - Pleroma.Repo.delete(recipient) - User.invalidate_cache(recipient) - - result = - conn - |> get("/api/v1/pleroma/chats") - |> json_response_and_validate_schema(200) - - assert length(result) == 0 - end - - test "it does not return chats with users you blocked", %{conn: conn, user: user} do - recipient = insert(:user) - - {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id) - - result = - conn - |> get("/api/v1/pleroma/chats") - |> json_response_and_validate_schema(200) - - assert length(result) == 1 - - User.block(user, recipient) - - result = - conn - |> get("/api/v1/pleroma/chats") - |> json_response_and_validate_schema(200) - - assert length(result) == 0 - end - - test "it does not return chats with users you muted", %{conn: conn, user: user} do - recipient = insert(:user) - - {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id) - - result = - conn - |> get("/api/v1/pleroma/chats") - |> json_response_and_validate_schema(200) - - assert length(result) == 1 - - User.mute(user, recipient) - - result = - conn - |> get("/api/v1/pleroma/chats") - |> json_response_and_validate_schema(200) - - assert length(result) == 0 - - result = - conn - |> get("/api/v1/pleroma/chats?with_muted=true") - |> json_response_and_validate_schema(200) - - assert length(result) == 1 - end - - test "it returns all chats", %{conn: conn, user: user} do - Enum.each(1..30, fn _ -> + test "it does not return chats with deleted users", %{conn: conn, user: user} do recipient = insert(:user) {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id) - end) - result = - conn - |> get("/api/v1/pleroma/chats") - |> json_response_and_validate_schema(200) + Pleroma.Repo.delete(recipient) + User.invalidate_cache(recipient) - assert length(result) == 30 - end + result = + conn + |> get(unquote(tested_endpoint)) + |> json_response_and_validate_schema(200) - test "it return a list of chats the current user is participating in, in descending order of updates", - %{conn: conn, user: user} do - har = insert(:user) - jafnhar = insert(:user) - tridi = insert(:user) + assert length(result) == 0 + end - {:ok, chat_1} = Chat.get_or_create(user.id, har.ap_id) - :timer.sleep(1000) - {:ok, _chat_2} = Chat.get_or_create(user.id, jafnhar.ap_id) - :timer.sleep(1000) - {:ok, chat_3} = Chat.get_or_create(user.id, tridi.ap_id) - :timer.sleep(1000) + test "it does not return chats with users you blocked", %{conn: conn, user: user} do + recipient = insert(:user) - # bump the second one - {:ok, chat_2} = Chat.bump_or_create(user.id, jafnhar.ap_id) + {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id) - result = - conn - |> get("/api/v1/pleroma/chats") - |> json_response_and_validate_schema(200) + result = + conn + |> get(unquote(tested_endpoint)) + |> json_response_and_validate_schema(200) - ids = Enum.map(result, & &1["id"]) + assert length(result) == 1 - assert ids == [ - chat_2.id |> to_string(), - chat_3.id |> to_string(), - chat_1.id |> to_string() - ] - end + User.block(user, recipient) - test "it is not affected by :restrict_unauthenticated setting (issue #1973)", %{ - conn: conn, - user: user - } do - clear_config([:restrict_unauthenticated, :profiles, :local], true) - clear_config([:restrict_unauthenticated, :profiles, :remote], true) + result = + conn + |> get(unquote(tested_endpoint)) + |> json_response_and_validate_schema(200) - user2 = insert(:user) - user3 = insert(:user, local: false) + assert length(result) == 0 + end - {:ok, _chat_12} = Chat.get_or_create(user.id, user2.ap_id) - {:ok, _chat_13} = Chat.get_or_create(user.id, user3.ap_id) + test "it does not return chats with users you muted", %{conn: conn, user: user} do + recipient = insert(:user) - result = - conn - |> get("/api/v1/pleroma/chats") - |> json_response_and_validate_schema(200) + {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id) - account_ids = Enum.map(result, &get_in(&1, ["account", "id"])) - assert Enum.sort(account_ids) == Enum.sort([user2.id, user3.id]) + result = + conn + |> get(unquote(tested_endpoint)) + |> json_response_and_validate_schema(200) + + assert length(result) == 1 + + User.mute(user, recipient) + + result = + conn + |> get(unquote(tested_endpoint)) + |> json_response_and_validate_schema(200) + + assert length(result) == 0 + + result = + conn + |> get("#{unquote(tested_endpoint)}?with_muted=true") + |> json_response_and_validate_schema(200) + + assert length(result) == 1 + end + + if tested_endpoint == "/api/v1/pleroma/chats" do + test "it returns all chats", %{conn: conn, user: user} do + Enum.each(1..30, fn _ -> + recipient = insert(:user) + {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id) + end) + + result = + conn + |> get(unquote(tested_endpoint)) + |> json_response_and_validate_schema(200) + + assert length(result) == 30 + end + else + test "it paginates chats", %{conn: conn, user: user} do + Enum.each(1..30, fn _ -> + recipient = insert(:user) + {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id) + end) + + result = + conn + |> get(unquote(tested_endpoint)) + |> json_response_and_validate_schema(200) + + assert length(result) == 20 + last_id = List.last(result)["id"] + + result = + conn + |> get(unquote(tested_endpoint) <> "?max_id=#{last_id}") + |> json_response_and_validate_schema(200) + + assert length(result) == 10 + end + end + + test "it return a list of chats the current user is participating in, in descending order of updates", + %{conn: conn, user: user} do + har = insert(:user) + jafnhar = insert(:user) + tridi = insert(:user) + + {:ok, chat_1} = Chat.get_or_create(user.id, har.ap_id) + {:ok, chat_1} = time_travel(chat_1, -3) + {:ok, chat_2} = Chat.get_or_create(user.id, jafnhar.ap_id) + {:ok, _chat_2} = time_travel(chat_2, -2) + {:ok, chat_3} = Chat.get_or_create(user.id, tridi.ap_id) + {:ok, chat_3} = time_travel(chat_3, -1) + + # bump the second one + {:ok, chat_2} = Chat.bump_or_create(user.id, jafnhar.ap_id) + + result = + conn + |> get(unquote(tested_endpoint)) + |> json_response_and_validate_schema(200) + + ids = Enum.map(result, & &1["id"]) + + assert ids == [ + chat_2.id |> to_string(), + chat_3.id |> to_string(), + chat_1.id |> to_string() + ] + end + + test "it is not affected by :restrict_unauthenticated setting (issue #1973)", %{ + conn: conn, + user: user + } do + clear_config([:restrict_unauthenticated, :profiles, :local], true) + clear_config([:restrict_unauthenticated, :profiles, :remote], true) + + user2 = insert(:user) + user3 = insert(:user, local: false) + + {:ok, _chat_12} = Chat.get_or_create(user.id, user2.ap_id) + {:ok, _chat_13} = Chat.get_or_create(user.id, user3.ap_id) + + result = + conn + |> get(unquote(tested_endpoint)) + |> json_response_and_validate_schema(200) + + account_ids = Enum.map(result, &get_in(&1, ["account", "id"])) + assert Enum.sort(account_ids) == Enum.sort([user2.id, user3.id]) + end end end end diff --git a/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs index f2feeaaef..54f2c5a58 100644 --- a/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ConversationControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Conversation.Participation alias Pleroma.Repo @@ -104,7 +104,7 @@ test "PATCH /api/v1/pleroma/conversations/:id" do [participation] = Participation.for_user(user) participation = Repo.preload(participation, :recipients) - assert user in participation.recipients + assert refresh_record(user) in participation.recipients assert other_user in participation.recipients end diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_file_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_file_controller_test.exs index 82de86ee3..547391249 100644 --- a/test/pleroma/web/pleroma_api/controllers/emoji_file_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/emoji_file_controller_test.exs @@ -1,10 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do use Pleroma.Web.ConnCase + import Mock import Tesla.Mock import Pleroma.Factory @@ -12,8 +13,6 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do Pleroma.Config.get!([:instance, :static_dir]), "emoji" ) - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) - setup do: clear_config([:instance, :public], true) setup do @@ -200,6 +199,31 @@ test "add file with not loaded pack", %{admin_conn: admin_conn} do } end + test "returns an error on add file when file system is not writable", %{ + admin_conn: admin_conn + } do + pack_file = Path.join([@emoji_path, "not_loaded", "pack.json"]) + + with_mocks([ + {File, [:passthrough], [stat: fn ^pack_file -> {:error, :eacces} end]} + ]) do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/files?name=not_loaded", %{ + shortcode: "blank3", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(500) == %{ + "error" => + "Unexpected error occurred while adding file to pack. (POSIX error: Permission denied)" + } + end + end + test "remove file with not loaded pack", %{admin_conn: admin_conn} do assert admin_conn |> delete("/api/pleroma/emoji/packs/files?name=not_loaded&shortcode=blank3") diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs index 3445f0ca0..d1ba067b8 100644 --- a/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs @@ -1,10 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: false + import Mock import Tesla.Mock import Pleroma.Factory @@ -12,7 +13,6 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do Pleroma.Config.get!([:instance, :static_dir]), "emoji" ) - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) setup do: clear_config([:instance, :public], true) @@ -30,7 +30,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do end test "GET /api/pleroma/emoji/packs when :public: false", %{conn: conn} do - Config.put([:instance, :public], false) + clear_config([:instance, :public], false) conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) end @@ -346,7 +346,7 @@ test "other error", %{admin_conn: admin_conn} do end end - describe "PATCH /api/pleroma/emoji/pack?name=:name" do + describe "PATCH/update /api/pleroma/emoji/pack?name=:name" do setup do pack_file = "#{@emoji_path}/test_pack/pack.json" original_content = File.read!(pack_file) @@ -365,6 +365,20 @@ test "other error", %{admin_conn: admin_conn} do }} end + test "returns error when file system not writable", %{admin_conn: conn} = ctx do + with_mocks([ + {File, [:passthrough], [stat: fn _ -> {:error, :eacces} end]} + ]) do + assert conn + |> put_req_header("content-type", "multipart/form-data") + |> patch( + "/api/pleroma/emoji/pack?name=test_pack", + %{"metadata" => ctx[:new_data]} + ) + |> json_response_and_validate_schema(500) + end + end + test "for a pack without a fallback source", ctx do assert ctx[:admin_conn] |> put_req_header("content-type", "multipart/form-data") @@ -424,6 +438,46 @@ test "when the fallback source doesn't have all the files", ctx do end describe "POST/DELETE /api/pleroma/emoji/pack?name=:name" do + test "returns an error on creates pack when file system not writable", %{ + admin_conn: admin_conn + } do + path_pack = Path.join(@emoji_path, "test_pack") + + with_mocks([ + {File, [:passthrough], [mkdir: fn ^path_pack -> {:error, :eacces} end]} + ]) do + assert admin_conn + |> post("/api/pleroma/emoji/pack?name=test_pack") + |> json_response_and_validate_schema(500) == %{ + "error" => + "Unexpected error occurred while creating pack. (POSIX error: Permission denied)" + } + end + end + + test "returns an error on deletes pack when the file system is not writable", %{ + admin_conn: admin_conn + } do + path_pack = Path.join(@emoji_path, "test_emoji_pack") + + try do + {:ok, _pack} = Pleroma.Emoji.Pack.create("test_emoji_pack") + + with_mocks([ + {File, [:passthrough], [rm_rf: fn ^path_pack -> {:error, :eacces, path_pack} end]} + ]) do + assert admin_conn + |> delete("/api/pleroma/emoji/pack?name=test_emoji_pack") + |> json_response_and_validate_schema(500) == %{ + "error" => + "Couldn't delete the `test_emoji_pack` pack (POSIX error: Permission denied)" + } + end + after + File.rm_rf(path_pack) + end + end + test "creating and deleting a pack", %{admin_conn: admin_conn} do assert admin_conn |> post("/api/pleroma/emoji/pack?name=test_created") diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs index bda9c20c6..28483985c 100644 --- a/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do diff --git a/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs index 13491ed9c..54cf9d083 100644 --- a/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaApi.InstancesControllerTest do diff --git a/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs index 289119d45..0011ddd54 100644 --- a/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.MascotControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.User diff --git a/test/pleroma/web/pleroma_api/controllers/notification_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/notification_controller_test.exs index bb4fe6c49..08f374908 100644 --- a/test/pleroma/web/pleroma_api/controllers/notification_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/notification_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.NotificationControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Notification alias Pleroma.Repo diff --git a/test/pleroma/web/pleroma_api/controllers/report_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/report_controller_test.exs new file mode 100644 index 000000000..c507aeca0 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/report_controller_test.exs @@ -0,0 +1,80 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.ReportControllerTest do + use Pleroma.Web.ConnCase, async: true + + import Pleroma.Factory + + alias Pleroma.Web.CommonAPI + + describe "GET /api/v0/pleroma/reports" do + test "returns list of own reports" do + %{conn: reporter_conn, user: reporter} = oauth_access(["read:reports"]) + %{conn: reported_conn, user: reported} = oauth_access(["read:reports"]) + activity = insert(:note_activity, user: reported) + + {:ok, %{id: report_id}} = + CommonAPI.report(reporter, %{ + account_id: reported.id, + comment: "You stole my sandwich!", + status_ids: [activity.id] + }) + + assert reported_response = + reported_conn + |> get("/api/v0/pleroma/reports") + |> json_response_and_validate_schema(:ok) + + assert reported_response == %{"reports" => [], "total" => 0} + + assert reporter_response = + reporter_conn + |> get("/api/v0/pleroma/reports") + |> json_response_and_validate_schema(:ok) + + assert %{"reports" => [report], "total" => 1} = reporter_response + assert report["id"] == report_id + refute report["notes"] + end + end + + describe "GET /api/v0/pleroma/reports/:id" do + test "returns report by its id" do + %{conn: reporter_conn, user: reporter} = oauth_access(["read:reports"]) + %{conn: reported_conn, user: reported} = oauth_access(["read:reports"]) + activity = insert(:note_activity, user: reported) + + {:ok, %{id: report_id}} = + CommonAPI.report(reporter, %{ + account_id: reported.id, + comment: "You stole my sandwich!", + status_ids: [activity.id] + }) + + assert reported_conn + |> get("/api/v0/pleroma/reports/#{report_id}") + |> json_response_and_validate_schema(:not_found) + + assert response = + reporter_conn + |> get("/api/v0/pleroma/reports/#{report_id}") + |> json_response_and_validate_schema(:ok) + + assert response["id"] == report_id + refute response["notes"] + end + + test "returns 404 when report id is invalid" do + %{conn: conn, user: _user} = oauth_access(["read:reports"]) + + assert response = + conn + |> get("/api/v0/pleroma/reports/0") + |> json_response_and_validate_schema(:not_found) + + assert response == %{"error" => "Record not found"} + end + end +end diff --git a/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs index f39c07ac6..d4546f442 100644 --- a/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Web.CommonAPI diff --git a/test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs index 22988c881..24074f4e5 100644 --- a/test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true import Pleroma.Factory alias Pleroma.MFA.Settings diff --git a/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs index d83d33912..25a7f8374 100644 --- a/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do diff --git a/test/pleroma/web/pleroma_api/views/backup_view_test.exs b/test/pleroma/web/pleroma_api/views/backup_view_test.exs new file mode 100644 index 000000000..9b4298dd1 --- /dev/null +++ b/test/pleroma/web/pleroma_api/views/backup_view_test.exs @@ -0,0 +1,18 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.BackupViewTest do + use Pleroma.DataCase, async: true + alias Pleroma.User.Backup + alias Pleroma.Web.PleromaAPI.BackupView + import Pleroma.Factory + + test "it renders the ID" do + user = insert(:user) + backup = Backup.new(user) + + result = BackupView.render("show.json", backup: backup) + assert result.id == backup.id + end +end diff --git a/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs index 93eef00a2..6deaa2102 100644 --- a/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs +++ b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do @@ -31,7 +31,7 @@ test "it displays a chat message" do chat = Chat.get(user.id, recipient.ap_id) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) cm_ref = MessageReference.for_chat_and_object(chat, object) @@ -58,7 +58,7 @@ test "it displays a chat message" do media_id: upload.id ) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) cm_ref = MessageReference.for_chat_and_object(chat, object) diff --git a/test/pleroma/web/pleroma_api/views/chat_view_test.exs b/test/pleroma/web/pleroma_api/views/chat_view_test.exs index 02484b705..5456c2de0 100644 --- a/test/pleroma/web/pleroma_api/views/chat_view_test.exs +++ b/test/pleroma/web/pleroma_api/views/chat_view_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ChatViewTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Chat alias Pleroma.Chat.MessageReference @@ -35,7 +35,7 @@ test "it represents a chat" do {:ok, chat_message_creation} = CommonAPI.post_chat_message(user, recipient, "hello") - chat_message = Object.normalize(chat_message_creation, false) + chat_message = Object.normalize(chat_message_creation, fetch: false) {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id) diff --git a/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs b/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs index 0f43cbdc3..382051f6f 100644 --- a/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs +++ b/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ScrobbleViewTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.PleromaAPI.ScrobbleView diff --git a/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs b/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs index 23498badf..79561afb7 100644 --- a/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs +++ b/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlugTest do @@ -35,7 +35,7 @@ test "does nothing if a user is assigned", %{conn: conn} do end test "with `admin_token` query parameter", %{conn: conn} do - Pleroma.Config.put(:admin_token, "password123") + clear_config(:admin_token, "password123") conn = %{conn | params: %{"admin_token" => "wrong_password"}} @@ -54,7 +54,7 @@ test "with `admin_token` query parameter", %{conn: conn} do end test "with `x-admin-token` HTTP header", %{conn: conn} do - Pleroma.Config.put(:admin_token, "☕️") + clear_config(:admin_token, "☕️") conn = conn diff --git a/test/pleroma/web/plugs/authentication_plug_test.exs b/test/pleroma/web/plugs/authentication_plug_test.exs index 3dedd38b2..118ab302a 100644 --- a/test/pleroma/web/plugs/authentication_plug_test.exs +++ b/test/pleroma/web/plugs/authentication_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do @@ -17,7 +17,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do user = %User{ id: 1, name: "dude", - password_hash: Pbkdf2.hash_pwd_salt("guy") + password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("guy") } conn = diff --git a/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs b/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs index 2d6af228c..e90078eb5 100644 --- a/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs +++ b/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.BasicAuthDecoderPlugTest do diff --git a/test/pleroma/web/plugs/cache_control_test.exs b/test/pleroma/web/plugs/cache_control_test.exs index fcf3d2be8..263961897 100644 --- a/test/pleroma/web/plugs/cache_control_test.exs +++ b/test/pleroma/web/plugs/cache_control_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.CacheControlTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Plug.Conn test "Verify Cache-Control header on static assets", %{conn: conn} do diff --git a/test/pleroma/web/plugs/cache_test.exs b/test/pleroma/web/plugs/cache_test.exs index 93a66f5d3..0ceab6cab 100644 --- a/test/pleroma/web/plugs/cache_test.exs +++ b/test/pleroma/web/plugs/cache_test.exs @@ -1,9 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.CacheTest do - use ExUnit.Case, async: true + # Relies on Cachex, has to stay synchronous + use Pleroma.DataCase use Plug.Test alias Pleroma.Web.Plugs.Cache @@ -24,11 +25,6 @@ defmodule Pleroma.Web.Plugs.CacheTest do @ttl 5 - setup do - Cachex.clear(:web_resp_cache) - :ok - end - test "caches a response" do assert @miss_resp == conn(:get, "/") diff --git a/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs b/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs index 92ff19282..6b3ee3d87 100644 --- a/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs +++ b/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.EnsureAuthenticatedPlugTest do diff --git a/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs b/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs index 211443a55..75c3b5784 100644 --- a/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs +++ b/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs @@ -1,18 +1,17 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlugTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase - alias Pleroma.Config alias Pleroma.User alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug setup do: clear_config([:instance, :public]) test "it halts if not public and no user is assigned", %{conn: conn} do - Config.put([:instance, :public], false) + clear_config([:instance, :public], false) conn = conn @@ -23,7 +22,7 @@ test "it halts if not public and no user is assigned", %{conn: conn} do end test "it continues if public", %{conn: conn} do - Config.put([:instance, :public], true) + clear_config([:instance, :public], true) ret_conn = conn @@ -33,7 +32,7 @@ test "it continues if public", %{conn: conn} do end test "it continues if a user is assigned, even if not public", %{conn: conn} do - Config.put([:instance, :public], false) + clear_config([:instance, :public], false) conn = conn diff --git a/test/pleroma/web/plugs/ensure_user_token_assigns_plug_test.exs b/test/pleroma/web/plugs/ensure_user_token_assigns_plug_test.exs index 9592820c7..28ec67158 100644 --- a/test/pleroma/web/plugs/ensure_user_token_assigns_plug_test.exs +++ b/test/pleroma/web/plugs/ensure_user_token_assigns_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.EnsureUserTokenAssignsPlugTest do diff --git a/test/pleroma/web/plugs/federating_plug_test.exs b/test/pleroma/web/plugs/federating_plug_test.exs index a4652f6c5..01ecd2a1e 100644 --- a/test/pleroma/web/plugs/federating_plug_test.exs +++ b/test/pleroma/web/plugs/federating_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.FederatingPlugTest do @@ -8,7 +8,7 @@ defmodule Pleroma.Web.Plugs.FederatingPlugTest do setup do: clear_config([:instance, :federating]) test "returns and halt the conn when federating is disabled" do - Pleroma.Config.put([:instance, :federating], false) + clear_config([:instance, :federating], false) conn = build_conn() @@ -19,7 +19,7 @@ test "returns and halt the conn when federating is disabled" do end test "does nothing when federating is enabled" do - Pleroma.Config.put([:instance, :federating], true) + clear_config([:instance, :federating], true) conn = build_conn() diff --git a/test/pleroma/web/plugs/frontend_static_plug_test.exs b/test/pleroma/web/plugs/frontend_static_plug_test.exs index 8b7b022fc..100b83d6a 100644 --- a/test/pleroma/web/plugs/frontend_static_plug_test.exs +++ b/test/pleroma/web/plugs/frontend_static_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do @@ -74,4 +74,35 @@ test "exclude invalid path", %{conn: conn} do assert %Plug.Conn{status: :success} = get(conn, url) end end + + test "api routes are detected correctly" do + # If this test fails we have probably added something + # new that should be in /api/ instead + expected_routes = [ + "api", + "main", + "ostatus_subscribe", + "oauth", + "objects", + "activities", + "notice", + "users", + "tags", + "mailer", + "inbox", + "relay", + "internal", + ".well-known", + "nodeinfo", + "web", + "auth", + "embed", + "proxy", + "test", + "user_exists", + "check_password" + ] + + assert expected_routes == Pleroma.Web.get_api_routes() + end end diff --git a/test/pleroma/web/plugs/http_security_plug_test.exs b/test/pleroma/web/plugs/http_security_plug_test.exs index df2b5ebb3..4e7befdd5 100644 --- a/test/pleroma/web/plugs/http_security_plug_test.exs +++ b/test/pleroma/web/plugs/http_security_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do @@ -72,6 +72,21 @@ test "default values for img-src and media-src with disabled media proxy", %{con assert csp =~ "media-src 'self' https:;" assert csp =~ "img-src 'self' data: blob: https:;" end + + test "it sets the Service-Worker-Allowed header", %{conn: conn} do + clear_config([:http_security, :enabled], true) + clear_config([:frontends, :primary], %{"name" => "fedi-fe", "ref" => "develop"}) + + clear_config([:frontends, :available], %{ + "fedi-fe" => %{ + "name" => "fedi-fe", + "custom-http-headers" => [{"service-worker-allowed", "/"}] + } + }) + + conn = get(conn, "/api/v1/instance") + assert Conn.get_resp_header(conn, "service-worker-allowed") == ["/"] + end end describe "img-src and media-src" do diff --git a/test/pleroma/web/plugs/http_signature_plug_test.exs b/test/pleroma/web/plugs/http_signature_plug_test.exs index e6cbde803..56ef6b06f 100644 --- a/test/pleroma/web/plugs/http_signature_plug_test.exs +++ b/test/pleroma/web/plugs/http_signature_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.HTTPSignaturePlugTest do @@ -32,11 +32,7 @@ test "it call HTTPSignatures to check validity if the actor sighed it" do describe "requires a signature when `authorized_fetch_mode` is enabled" do setup do - Pleroma.Config.put([:activitypub, :authorized_fetch_mode], true) - - on_exit(fn -> - Pleroma.Config.put([:activitypub, :authorized_fetch_mode], false) - end) + clear_config([:activitypub, :authorized_fetch_mode], true) params = %{"actor" => "http://mastodon.example.org/users/admin"} conn = build_conn(:get, "/doesntmattter", params) |> put_format("activity+json") diff --git a/test/pleroma/web/plugs/idempotency_plug_test.exs b/test/pleroma/web/plugs/idempotency_plug_test.exs index 4a7835993..dd8cda664 100644 --- a/test/pleroma/web/plugs/idempotency_plug_test.exs +++ b/test/pleroma/web/plugs/idempotency_plug_test.exs @@ -1,9 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.IdempotencyPlugTest do - use ExUnit.Case, async: true + # Relies on Cachex, has to stay synchronous + use Pleroma.DataCase use Plug.Test alias Pleroma.Web.Plugs.IdempotencyPlug diff --git a/test/pleroma/web/plugs/instance_static_test.exs b/test/pleroma/web/plugs/instance_static_test.exs index 5b30011d3..46f2ca6b1 100644 --- a/test/pleroma/web/plugs/instance_static_test.exs +++ b/test/pleroma/web/plugs/instance_static_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.InstanceStaticTest do diff --git a/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs b/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs index 0ad3c2929..00ce6492d 100644 --- a/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs +++ b/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlugTest do diff --git a/test/pleroma/web/plugs/o_auth_plug_test.exs b/test/pleroma/web/plugs/o_auth_plug_test.exs index 1186cdb14..9e4be5559 100644 --- a/test/pleroma/web/plugs/o_auth_plug_test.exs +++ b/test/pleroma/web/plugs/o_auth_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.OAuthPlugTest do diff --git a/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs b/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs index 982a70bf9..9f6d3dc71 100644 --- a/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs +++ b/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.OAuthScopesPlugTest do @@ -169,42 +169,4 @@ test "filters scopes which directly match or are ancestors of supported scopes" assert f.(["admin:read"], ["write", "admin"]) == ["admin:read"] end end - - describe "transform_scopes/2" do - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage]) - - setup do - {:ok, %{f: &OAuthScopesPlug.transform_scopes/2}} - end - - test "with :admin option, prefixes all requested scopes with `admin:` " <> - "and [optionally] keeps only prefixed scopes, " <> - "depending on `[:auth, :enforce_oauth_admin_scope_usage]` setting", - %{f: f} do - Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false) - - assert f.(["read"], %{admin: true}) == ["admin:read", "read"] - - assert f.(["read", "write"], %{admin: true}) == [ - "admin:read", - "read", - "admin:write", - "write" - ] - - Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], true) - - assert f.(["read:accounts"], %{admin: true}) == ["admin:read:accounts"] - - assert f.(["read", "write:reports"], %{admin: true}) == [ - "admin:read", - "admin:write:reports" - ] - end - - test "with no supported options, returns unmodified scopes", %{f: f} do - assert f.(["read"], %{}) == ["read"] - assert f.(["read", "write"], %{}) == ["read", "write"] - end - end end diff --git a/test/pleroma/web/plugs/plug_helper_test.exs b/test/pleroma/web/plugs/plug_helper_test.exs index 670d699f0..346113628 100644 --- a/test/pleroma/web/plugs/plug_helper_test.exs +++ b/test/pleroma/web/plugs/plug_helper_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.PlugHelperTest do diff --git a/test/pleroma/web/plugs/rate_limiter_test.exs b/test/pleroma/web/plugs/rate_limiter_test.exs index 249c78b37..d007e3f26 100644 --- a/test/pleroma/web/plugs/rate_limiter_test.exs +++ b/test/pleroma/web/plugs/rate_limiter_test.exs @@ -1,12 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.RateLimiterTest do use Pleroma.Web.ConnCase alias Phoenix.ConnTest - alias Pleroma.Config alias Pleroma.Web.Plugs.RateLimiter alias Plug.Conn @@ -22,8 +21,8 @@ defmodule Pleroma.Web.Plugs.RateLimiterTest do setup do: clear_config([Pleroma.Web.Plugs.RemoteIp, :enabled]) test "config is required for plug to work" do - Config.put([:rate_limit, @limiter_name], {1, 1}) - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + clear_config([:rate_limit, @limiter_name], {1, 1}) + clear_config([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) assert %{limits: {1, 1}, name: :test_init, opts: [name: :test_init]} == [name: @limiter_name] @@ -54,8 +53,8 @@ test "it restricts based on config values" do scale = 80 limit = 5 - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) - Config.put([:rate_limit, limiter_name], {scale, limit}) + clear_config([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + clear_config([:rate_limit, limiter_name], {scale, limit}) plug_opts = RateLimiter.init(name: limiter_name) conn = build_conn(:get, "/") @@ -86,8 +85,8 @@ test "it restricts based on config values" do test "`bucket_name` option overrides default bucket name" do limiter_name = :test_bucket_name - Config.put([:rate_limit, limiter_name], {1000, 5}) - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + clear_config([:rate_limit, limiter_name], {1000, 5}) + clear_config([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) base_bucket_name = "#{limiter_name}:group1" plug_opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name) @@ -101,8 +100,8 @@ test "`bucket_name` option overrides default bucket name" do test "`params` option allows different queries to be tracked independently" do limiter_name = :test_params - Config.put([:rate_limit, limiter_name], {1000, 5}) - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + clear_config([:rate_limit, limiter_name], {1000, 5}) + clear_config([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) plug_opts = RateLimiter.init(name: limiter_name, params: ["id"]) @@ -117,8 +116,8 @@ test "`params` option allows different queries to be tracked independently" do test "it supports combination of options modifying bucket name" do limiter_name = :test_options_combo - Config.put([:rate_limit, limiter_name], {1000, 5}) - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + clear_config([:rate_limit, limiter_name], {1000, 5}) + clear_config([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) base_bucket_name = "#{limiter_name}:group1" @@ -140,8 +139,8 @@ test "it supports combination of options modifying bucket name" do describe "unauthenticated users" do test "are restricted based on remote IP" do limiter_name = :test_unauthenticated - Config.put([:rate_limit, limiter_name], [{1000, 5}, {1, 10}]) - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + clear_config([:rate_limit, limiter_name], [{1000, 5}, {1, 10}]) + clear_config([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) plug_opts = RateLimiter.init(name: limiter_name) @@ -180,8 +179,8 @@ test "can have limits separate from unauthenticated connections" do scale = 50 limit = 5 - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) - Config.put([:rate_limit, limiter_name], [{1000, 1}, {scale, limit}]) + clear_config([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + clear_config([:rate_limit, limiter_name], [{1000, 1}, {scale, limit}]) plug_opts = RateLimiter.init(name: limiter_name) @@ -202,8 +201,8 @@ test "can have limits separate from unauthenticated connections" do test "different users are counted independently" do limiter_name = :test_authenticated2 - Config.put([:rate_limit, limiter_name], [{1, 10}, {1000, 5}]) - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + clear_config([:rate_limit, limiter_name], [{1, 10}, {1000, 5}]) + clear_config([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) plug_opts = RateLimiter.init(name: limiter_name) @@ -232,8 +231,8 @@ test "different users are counted independently" do test "doesn't crash due to a race condition when multiple requests are made at the same time and the bucket is not yet initialized" do limiter_name = :test_race_condition - Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5}) - Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + clear_config([:rate_limit, limiter_name], {1000, 5}) + clear_config([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) opts = RateLimiter.init(name: limiter_name) diff --git a/test/pleroma/web/plugs/remote_ip_test.exs b/test/pleroma/web/plugs/remote_ip_test.exs index 0bdb4c168..4d98de2bd 100644 --- a/test/pleroma/web/plugs/remote_ip_test.exs +++ b/test/pleroma/web/plugs/remote_ip_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.RemoteIpTest do @@ -26,7 +26,7 @@ defmodule Pleroma.Web.Plugs.RemoteIpTest do ) test "disabled" do - Pleroma.Config.put(RemoteIp, enabled: false) + clear_config(RemoteIp, enabled: false) %{remote_ip: remote_ip} = conn(:get, "/") @@ -48,7 +48,7 @@ test "enabled" do end test "custom headers" do - Pleroma.Config.put(RemoteIp, enabled: true, headers: ["cf-connecting-ip"]) + clear_config(RemoteIp, enabled: true, headers: ["cf-connecting-ip"]) conn = conn(:get, "/") @@ -73,7 +73,7 @@ test "custom proxies" do refute conn.remote_ip == {1, 1, 1, 1} - Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.0/20"]) + clear_config([RemoteIp, :proxies], ["173.245.48.0/20"]) conn = conn(:get, "/") @@ -84,7 +84,7 @@ test "custom proxies" do end test "proxies set without CIDR format" do - Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.1"]) + clear_config([RemoteIp, :proxies], ["173.245.48.1"]) conn = conn(:get, "/") @@ -95,8 +95,8 @@ test "proxies set without CIDR format" do end test "proxies set `nonsensical` CIDR" do - Pleroma.Config.put([RemoteIp, :reserved], ["127.0.0.0/8"]) - Pleroma.Config.put([RemoteIp, :proxies], ["10.0.0.3/24"]) + clear_config([RemoteIp, :reserved], ["127.0.0.0/8"]) + clear_config([RemoteIp, :proxies], ["10.0.0.3/24"]) conn = conn(:get, "/") diff --git a/test/pleroma/web/plugs/set_format_plug_test.exs b/test/pleroma/web/plugs/set_format_plug_test.exs index e95d751fa..21043f698 100644 --- a/test/pleroma/web/plugs/set_format_plug_test.exs +++ b/test/pleroma/web/plugs/set_format_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.SetFormatPlugTest do diff --git a/test/pleroma/web/plugs/set_locale_plug_test.exs b/test/pleroma/web/plugs/set_locale_plug_test.exs index 773f48a5b..5261e67ae 100644 --- a/test/pleroma/web/plugs/set_locale_plug_test.exs +++ b/test/pleroma/web/plugs/set_locale_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.SetLocalePlugTest do diff --git a/test/pleroma/web/plugs/set_user_session_id_plug_test.exs b/test/pleroma/web/plugs/set_user_session_id_plug_test.exs index 21417d0e7..9814c80d8 100644 --- a/test/pleroma/web/plugs/set_user_session_id_plug_test.exs +++ b/test/pleroma/web/plugs/set_user_session_id_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.SetUserSessionIdPlugTest do diff --git a/test/pleroma/web/plugs/uploaded_media_plug_test.exs b/test/pleroma/web/plugs/uploaded_media_plug_test.exs index 7c8313121..75f313282 100644 --- a/test/pleroma/web/plugs/uploaded_media_plug_test.exs +++ b/test/pleroma/web/plugs/uploaded_media_plug_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UploadedMediaPlugTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Upload defp upload_file(context) do diff --git a/test/pleroma/web/plugs/user_enabled_plug_test.exs b/test/pleroma/web/plugs/user_enabled_plug_test.exs index 71c56f03a..999c6c49c 100644 --- a/test/pleroma/web/plugs/user_enabled_plug_test.exs +++ b/test/pleroma/web/plugs/user_enabled_plug_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UserEnabledPlugTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase alias Pleroma.Web.Plugs.UserEnabledPlug import Pleroma.Factory @@ -20,9 +20,9 @@ test "doesn't do anything if the user isn't set", %{conn: conn} do test "with a user that's not confirmed and a config requiring confirmation, it removes that user", %{conn: conn} do - Pleroma.Config.put([:instance, :account_activation_required], true) + clear_config([:instance, :account_activation_required], true) - user = insert(:user, confirmation_pending: true) + user = insert(:user, is_confirmed: false) conn = conn @@ -33,7 +33,7 @@ test "with a user that's not confirmed and a config requiring confirmation, it r end test "with a user that is deactivated, it removes that user", %{conn: conn} do - user = insert(:user, deactivated: true) + user = insert(:user, is_active: false) conn = conn diff --git a/test/pleroma/web/plugs/user_fetcher_plug_test.exs b/test/pleroma/web/plugs/user_fetcher_plug_test.exs index b4f875d2d..902bee642 100644 --- a/test/pleroma/web/plugs/user_fetcher_plug_test.exs +++ b/test/pleroma/web/plugs/user_fetcher_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UserFetcherPlugTest do diff --git a/test/pleroma/web/plugs/user_is_admin_plug_test.exs b/test/pleroma/web/plugs/user_is_admin_plug_test.exs index b550568c1..58996d5a4 100644 --- a/test/pleroma/web/plugs/user_is_admin_plug_test.exs +++ b/test/pleroma/web/plugs/user_is_admin_plug_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Plugs.UserIsAdminPlugTest do diff --git a/test/pleroma/web/plugs/user_tracking_plug_test.exs b/test/pleroma/web/plugs/user_tracking_plug_test.exs new file mode 100644 index 000000000..8e9d59b99 --- /dev/null +++ b/test/pleroma/web/plugs/user_tracking_plug_test.exs @@ -0,0 +1,58 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.UserTrackingPlugTest do + use Pleroma.Web.ConnCase, async: true + + import Pleroma.Factory + + alias Pleroma.Web.Plugs.UserTrackingPlug + + test "updates last_active_at for a new user", %{conn: conn} do + user = insert(:user) + + assert is_nil(user.last_active_at) + + test_started_at = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + + %{assigns: %{user: user}} = + conn + |> assign(:user, user) + |> UserTrackingPlug.call(%{}) + + assert user.last_active_at >= test_started_at + assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second) + end + + test "doesn't update last_active_at if it was updated recently", %{conn: conn} do + last_active_at = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(-:timer.hours(1), :millisecond) + |> NaiveDateTime.truncate(:second) + + user = insert(:user, %{last_active_at: last_active_at}) + + %{assigns: %{user: user}} = + conn + |> assign(:user, user) + |> UserTrackingPlug.call(%{}) + + assert user.last_active_at == last_active_at + end + + test "skips updating last_active_at if user ID is nil", %{conn: conn} do + %{assigns: %{user: user}} = + conn + |> assign(:user, %Pleroma.User{}) + |> UserTrackingPlug.call(%{}) + + assert is_nil(user.last_active_at) + end + + test "does nothing if user is not present", %{conn: conn} do + %{assigns: assigns} = UserTrackingPlug.call(conn, %{}) + + refute Map.has_key?(assigns, :user) + end +end diff --git a/test/pleroma/web/preload/providers/instance_test.exs b/test/pleroma/web/preload/providers/instance_test.exs index 8493f2a94..a401475ee 100644 --- a/test/pleroma/web/preload/providers/instance_test.exs +++ b/test/pleroma/web/preload/providers/instance_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload.Providers.InstanceTest do @@ -50,7 +50,7 @@ test "it renders the frontend configurations", %{ "/api/pleroma/frontend_configurations" => fe_configs } do assert %{ - pleroma_fe: %{background: "/images/city.jpg", logo: "/static/logo.png"} + pleroma_fe: %{background: "/images/city.jpg", logo: "/static/logo.svg"} } = fe_configs end end diff --git a/test/pleroma/web/preload/providers/timeline_test.exs b/test/pleroma/web/preload/providers/timeline_test.exs index 3b1f2f1aa..2ae2ca5fb 100644 --- a/test/pleroma/web/preload/providers/timeline_test.exs +++ b/test/pleroma/web/preload/providers/timeline_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload.Providers.TimelineTest do diff --git a/test/pleroma/web/preload/providers/user_test.exs b/test/pleroma/web/preload/providers/user_test.exs index 83f065e27..b7017ac20 100644 --- a/test/pleroma/web/preload/providers/user_test.exs +++ b/test/pleroma/web/preload/providers/user_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload.Providers.UserTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Web.Preload.Providers.User diff --git a/test/pleroma/web/push/impl_test.exs b/test/pleroma/web/push/impl_test.exs index 2a4a8fd06..b3ca1a337 100644 --- a/test/pleroma/web/push/impl_test.exs +++ b/test/pleroma/web/push/impl_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Push.ImplTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory @@ -118,7 +118,7 @@ test "renders title and body for create activity" do "Lorem ipsum dolor sit amet, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis." }) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert Impl.format_body( %{ @@ -137,7 +137,7 @@ test "renders title and body for follow activity" do user = insert(:user, nickname: "Bob") other_user = insert(:user) {:ok, _, _, activity} = CommonAPI.follow(user, other_user) - object = Object.normalize(activity, false) + object = Object.normalize(activity, fetch: false) assert Impl.format_body(%{activity: activity, type: "follow"}, user, object) == "@Bob has followed you" @@ -156,7 +156,7 @@ test "renders title and body for announce activity" do }) {:ok, announce_activity} = CommonAPI.repeat(activity.id, user) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert Impl.format_body(%{activity: announce_activity}, user, object) == "@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..." @@ -175,7 +175,7 @@ test "renders title and body for like activity" do }) {:ok, activity} = CommonAPI.favorite(user, activity.id) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert Impl.format_body(%{activity: activity, type: "favourite"}, user, object) == "@Bob has favorited your post" @@ -193,7 +193,7 @@ test "renders title and body for pleroma:emoji_reaction activity" do }) {:ok, activity} = CommonAPI.react_with_emoji(activity.id, user, "👍") - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert Impl.format_body(%{activity: activity, type: "pleroma:emoji_reaction"}, user, object) == "@Bob reacted with 👍" @@ -221,7 +221,7 @@ test "builds content for chat messages" do recipient = insert(:user) {:ok, chat} = CommonAPI.post_chat_message(user, recipient, "hey") - object = Object.normalize(chat, false) + object = Object.normalize(chat, fetch: false) [notification] = Notification.for_user(recipient) res = Impl.build_content(notification, user, object) @@ -245,7 +245,7 @@ test "builds content for chat messages with no content" do {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) {:ok, chat} = CommonAPI.post_chat_message(user, recipient, nil, media_id: upload.id) - object = Object.normalize(chat, false) + object = Object.normalize(chat, fetch: false) [notification] = Notification.for_user(recipient) res = Impl.build_content(notification, user, object) @@ -271,7 +271,7 @@ test "hides contents of notifications when option enabled" do notif = insert(:notification, user: user2, activity: activity) actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert Impl.build_content(notif, actor, object) == %{ body: "New Direct Message" @@ -286,7 +286,7 @@ test "hides contents of notifications when option enabled" do notif = insert(:notification, user: user2, activity: activity, type: "mention") actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert Impl.build_content(notif, actor, object) == %{ body: "New Mention" @@ -297,7 +297,7 @@ test "hides contents of notifications when option enabled" do notif = insert(:notification, user: user2, activity: activity, type: "favourite") actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert Impl.build_content(notif, actor, object) == %{ body: "New Favorite" @@ -320,7 +320,7 @@ test "returns regular content when hiding contents option disabled" do notif = insert(:notification, user: user2, activity: activity) actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert Impl.build_content(notif, actor, object) == %{ body: @@ -338,7 +338,7 @@ test "returns regular content when hiding contents option disabled" do notif = insert(:notification, user: user2, activity: activity, type: "mention") actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert Impl.build_content(notif, actor, object) == %{ body: @@ -351,7 +351,7 @@ test "returns regular content when hiding contents option disabled" do notif = insert(:notification, user: user2, activity: activity, type: "favourite") actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) - object = Object.normalize(activity) + object = Object.normalize(activity, fetch: false) assert Impl.build_content(notif, actor, object) == %{ body: "@Bob has favorited your post", diff --git a/test/pleroma/web/rel_me_test.exs b/test/pleroma/web/rel_me_test.exs index 65255916d..313b163b5 100644 --- a/test/pleroma/web/rel_me_test.exs +++ b/test/pleroma/web/rel_me_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RelMeTest do - use ExUnit.Case + use Pleroma.DataCase setup_all do Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) diff --git a/test/pleroma/web/rich_media/helpers_test.exs b/test/pleroma/web/rich_media/helpers_test.exs index 4c9ee77d0..689854fb6 100644 --- a/test/pleroma/web/rich_media/helpers_test.exs +++ b/test/pleroma/web/rich_media/helpers_test.exs @@ -1,11 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.HelpersTest do use Pleroma.DataCase - alias Pleroma.Config alias Pleroma.Web.CommonAPI alias Pleroma.Web.RichMedia.Helpers @@ -29,7 +28,7 @@ test "refuses to crawl incomplete URLs" do content_type: "text/markdown" }) - Config.put([:rich_media, :enabled], true) + clear_config([:rich_media, :enabled], true) assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end @@ -43,7 +42,7 @@ test "refuses to crawl malformed URLs" do content_type: "text/markdown" }) - Config.put([:rich_media, :enabled], true) + clear_config([:rich_media, :enabled], true) assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end @@ -57,7 +56,7 @@ test "crawls valid, complete URLs" do content_type: "text/markdown" }) - Config.put([:rich_media, :enabled], true) + clear_config([:rich_media, :enabled], true) assert %{page_url: "https://example.com/ogp", rich_media: _} = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) @@ -74,7 +73,7 @@ test "refuses to crawl URLs of private network from posts" do {:ok, activity4} = CommonAPI.post(user, %{status: "https://192.168.10.40/notice/9kCP7V"}) {:ok, activity5} = CommonAPI.post(user, %{status: "https://pleroma.local/notice/9kCP7V"}) - Config.put([:rich_media, :enabled], true) + clear_config([:rich_media, :enabled], true) assert %{} = Helpers.fetch_data_for_activity(activity) assert %{} = Helpers.fetch_data_for_activity(activity2) diff --git a/test/pleroma/web/rich_media/parser/ttl/aws_signed_url_test.exs b/test/pleroma/web/rich_media/parser/ttl/aws_signed_url_test.exs index 2f17bebd7..df3ea3e99 100644 --- a/test/pleroma/web/rich_media/parser/ttl/aws_signed_url_test.exs +++ b/test/pleroma/web/rich_media/parser/ttl/aws_signed_url_test.exs @@ -1,9 +1,10 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrlTest do - use ExUnit.Case, async: true + # Relies on Cachex, needs to be synchronous + use Pleroma.DataCase test "s3 signed url is parsed correct for expiration time" do url = "https://pleroma.social/amz" diff --git a/test/pleroma/web/rich_media/parser_test.exs b/test/pleroma/web/rich_media/parser_test.exs index 6d00c2af5..2f363b012 100644 --- a/test/pleroma/web/rich_media/parser_test.exs +++ b/test/pleroma/web/rich_media/parser_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.ParserTest do diff --git a/test/pleroma/web/rich_media/parsers/twitter_card_test.exs b/test/pleroma/web/rich_media/parsers/twitter_card_test.exs index 219f005a2..2aacd29a3 100644 --- a/test/pleroma/web/rich_media/parsers/twitter_card_test.exs +++ b/test/pleroma/web/rich_media/parsers/twitter_card_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do diff --git a/test/pleroma/web/static_fe/static_fe_controller_test.exs b/test/pleroma/web/static_fe/static_fe_controller_test.exs index 19506f1d8..2af14dfeb 100644 --- a/test/pleroma/web/static_fe/static_fe_controller_test.exs +++ b/test/pleroma/web/static_fe/static_fe_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do diff --git a/test/pleroma/web/streamer_test.exs b/test/pleroma/web/streamer_test.exs index ad66ddc9d..b788a9138 100644 --- a/test/pleroma/web/streamer_test.exs +++ b/test/pleroma/web/streamer_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.StreamerTest do @@ -266,7 +266,7 @@ test "it sends chat messages to the 'user:pleroma_chat' stream", %{ {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno", idempotency_key: "123") - object = Object.normalize(create_activity, false) + object = Object.normalize(create_activity, fetch: false) chat = Chat.get(user.id, other_user.ap_id) cm_ref = MessageReference.for_chat_and_object(chat, object) cm_ref = %{cm_ref | chat: chat, object: object} @@ -284,7 +284,7 @@ test "it sends chat messages to the 'user' stream", %{user: user, token: oauth_t other_user = insert(:user) {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno") - object = Object.normalize(create_activity, false) + object = Object.normalize(create_activity, fetch: false) chat = Chat.get(user.id, other_user.ap_id) cm_ref = MessageReference.for_chat_and_object(chat, object) cm_ref = %{cm_ref | chat: chat, object: object} @@ -383,19 +383,8 @@ test "it sends follow activities to the 'user:notification' stream", %{ user: user, token: oauth_token } do - user_url = user.ap_id user2 = insert(:user) - body = - File.read!("test/fixtures/users_mock/localhost.json") - |> String.replace("{{nickname}}", user.nickname) - |> Jason.encode!() - - Tesla.Mock.mock_global(fn - %{method: :get, url: ^user_url} -> - %Tesla.Env{status: 200, body: body} - end) - Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) {:ok, _follower, _followed, follow_activity} = CommonAPI.follow(user2, user) @@ -409,20 +398,9 @@ test "it sends follow relationships updates to the 'user' stream", %{ token: oauth_token } do user_id = user.id - user_url = user.ap_id other_user = insert(:user) other_user_id = other_user.id - body = - File.read!("test/fixtures/users_mock/localhost.json") - |> String.replace("{{nickname}}", user.nickname) - |> Jason.encode!() - - Tesla.Mock.mock_global(fn - %{method: :get, url: ^user_url} -> - %Tesla.Env{status: 200, body: body} - end) - Streamer.get_topic_and_add_socket("user", user, oauth_token) {:ok, _follower, _followed, _follow_activity} = CommonAPI.follow(user, other_user) @@ -510,7 +488,7 @@ test "handles deletions" do describe "thread_containment/2" do test "it filters to user if recipients invalid and thread containment is enabled" do - Pleroma.Config.put([:instance, :skip_thread_containment], false) + clear_config([:instance, :skip_thread_containment], false) author = insert(:user) %{user: user, token: oauth_token} = oauth_access(["read"]) User.follow(user, author, :follow_accept) @@ -531,7 +509,7 @@ test "it filters to user if recipients invalid and thread containment is enabled end test "it sends message if recipients invalid and thread containment is disabled" do - Pleroma.Config.put([:instance, :skip_thread_containment], true) + clear_config([:instance, :skip_thread_containment], true) author = insert(:user) %{user: user, token: oauth_token} = oauth_access(["read"]) User.follow(user, author, :follow_accept) @@ -553,7 +531,7 @@ test "it sends message if recipients invalid and thread containment is disabled" end test "it sends message if recipients invalid and thread containment is enabled but user's thread containment is disabled" do - Pleroma.Config.put([:instance, :skip_thread_containment], false) + clear_config([:instance, :skip_thread_containment], false) author = insert(:user) user = insert(:user, skip_thread_containment: true) %{token: oauth_token} = oauth_access(["read"], user: user) diff --git a/test/pleroma/web/twitter_api/controller_test.exs b/test/pleroma/web/twitter_api/controller_test.exs index 464d0ea2e..583c904b2 100644 --- a/test/pleroma/web/twitter_api/controller_test.exs +++ b/test/pleroma/web/twitter_api/controller_test.exs @@ -1,13 +1,13 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.ControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true - alias Pleroma.Builders.ActivityBuilder alias Pleroma.Repo alias Pleroma.User + alias Pleroma.Web.CommonAPI alias Pleroma.Web.OAuth.Token import Pleroma.Factory @@ -36,22 +36,20 @@ test "with credentials, with params" do other_user = insert(:user) {:ok, _activity} = - ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user}) + CommonAPI.post(other_user, %{ + status: "Hey @#{current_user.nickname}" + }) response_conn = conn - |> assign(:user, current_user) |> get("/api/v1/notifications") - [notification] = response = json_response(response_conn, 200) - - assert length(response) == 1 + [notification] = json_response(response_conn, 200) assert notification["pleroma"]["is_seen"] == false response_conn = conn - |> assign(:user, current_user) |> post("/api/qvitter/statuses/notifications/read", %{"latest_id" => notification["id"]}) [notification] = response = json_response(response_conn, 200) @@ -66,10 +64,10 @@ test "with credentials, with params" do setup do {:ok, user} = insert(:user) - |> User.confirmation_changeset(need_confirmation: true) + |> User.confirmation_changeset(set_confirmation: false) |> Repo.update() - assert user.confirmation_pending + refute user.is_confirmed [user: user] end @@ -85,7 +83,7 @@ test "it confirms the user account", %{conn: conn, user: user} do user = User.get_cached_by_id(user.id) - refute user.confirmation_pending + assert user.is_confirmed refute user.confirmation_token end diff --git a/test/pleroma/web/twitter_api/password_controller_test.exs b/test/pleroma/web/twitter_api/password_controller_test.exs index 6d08075cc..cf99e2434 100644 --- a/test/pleroma/web/twitter_api/password_controller_test.exs +++ b/test/pleroma/web/twitter_api/password_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do @@ -37,8 +37,7 @@ test "it returns an error when the token has expired", %{conn: conn} do user = insert(:user) {:ok, token} = PasswordResetToken.create_token(user) - - :timer.sleep(2000) + {:ok, token} = time_travel(token, -2) response = conn @@ -55,7 +54,7 @@ test "it fails for an expired token", %{conn: conn} do user = insert(:user) {:ok, token} = PasswordResetToken.create_token(user) - :timer.sleep(2000) + {:ok, token} = time_travel(token, -2) {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{}) params = %{ @@ -93,7 +92,7 @@ test "it returns HTTP 200", %{conn: conn} do assert response =~ "

Password changed!

" user = refresh_record(user) - assert Pbkdf2.verify_pass("test", user.password_hash) + assert Pleroma.Password.Pbkdf2.verify_pass("test", user.password_hash) assert Enum.empty?(Token.get_user_tokens(user)) end diff --git a/test/pleroma/web/twitter_api/remote_follow_controller_test.exs b/test/pleroma/web/twitter_api/remote_follow_controller_test.exs index a3e784d13..fa3b29006 100644 --- a/test/pleroma/web/twitter_api/remote_follow_controller_test.exs +++ b/test/pleroma/web/twitter_api/remote_follow_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do @@ -14,18 +14,37 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do import Pleroma.Factory import Ecto.Query - setup do - Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - setup_all do: clear_config([:instance, :federating], true) - setup do: clear_config([:instance]) - setup do: clear_config([:frontend_configurations, :pleroma_fe]) setup do: clear_config([:user, :deny_follow_blocked]) describe "GET /ostatus_subscribe - remote_follow/2" do test "adds status to pleroma instance if the `acct` is a status", %{conn: conn} do + Tesla.Mock.mock(fn + %{method: :get, url: "https://mastodon.social/users/emelie/statuses/101849165031453009"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: File.read!("test/fixtures/tesla_mock/status.emelie.json") + } + + %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: + File.read!("test/fixtures/users_mock/masto_featured.json") + |> String.replace("{{domain}}", "mastodon.social") + |> String.replace("{{nickname}}", "emelie") + } + + %{method: :get, url: "https://mastodon.social/users/emelie"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: File.read!("test/fixtures/tesla_mock/emelie.json") + } + end) + assert conn |> get( remote_follow_path(conn, :follow, %{ @@ -36,6 +55,25 @@ test "adds status to pleroma instance if the `acct` is a status", %{conn: conn} end test "show follow account page if the `acct` is a account link", %{conn: conn} do + Tesla.Mock.mock(fn + %{method: :get, url: "https://mastodon.social/users/emelie"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: File.read!("test/fixtures/tesla_mock/emelie.json") + } + + %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: + File.read!("test/fixtures/users_mock/masto_featured.json") + |> String.replace("{{domain}}", "mastodon.social") + |> String.replace("{{nickname}}", "emelie") + } + end) + response = conn |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"})) @@ -45,6 +83,25 @@ test "show follow account page if the `acct` is a account link", %{conn: conn} d end test "show follow page if the `acct` is a account link", %{conn: conn} do + Tesla.Mock.mock(fn + %{method: :get, url: "https://mastodon.social/users/emelie"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: File.read!("test/fixtures/tesla_mock/emelie.json") + } + + %{method: :get, url: "https://mastodon.social/users/emelie/collections/featured"} -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/activity+json"}], + body: + File.read!("test/fixtures/users_mock/masto_featured.json") + |> String.replace("{{domain}}", "mastodon.social") + |> String.replace("{{nickname}}", "emelie") + } + end) + user = insert(:user) response = @@ -56,7 +113,14 @@ test "show follow page if the `acct` is a account link", %{conn: conn} do assert response =~ "Remote follow" end - test "show follow page with error when user cannot fecth by `acct` link", %{conn: conn} do + test "show follow page with error when user can not be fetched by `acct` link", %{conn: conn} do + Tesla.Mock.mock(fn + %{method: :get, url: "https://mastodon.social/users/not_found"} -> + %Tesla.Env{ + status: 404 + } + end) + user = insert(:user) assert capture_log(fn -> @@ -107,7 +171,7 @@ test "follows user", %{conn: conn} do end test "returns error when user is deactivated", %{conn: conn} do - user = insert(:user, deactivated: true) + user = insert(:user, is_active: false) user2 = insert(:user) response = @@ -120,7 +184,7 @@ test "returns error when user is deactivated", %{conn: conn} do end test "returns error when user is blocked", %{conn: conn} do - Pleroma.Config.put([:user, :deny_follow_blocked], true) + clear_config([:user, :deny_follow_blocked], true) user = insert(:user) user2 = insert(:user) @@ -331,7 +395,7 @@ test "returns error when password invalid", %{conn: conn} do end test "returns error when user is blocked", %{conn: conn} do - Pleroma.Config.put([:user, :deny_follow_blocked], true) + clear_config([:user, :deny_follow_blocked], true) user = insert(:user) user2 = insert(:user) {:ok, _user_block} = Pleroma.User.block(user2, user) diff --git a/test/pleroma/web/twitter_api/twitter_api_test.exs b/test/pleroma/web/twitter_api/twitter_api_test.exs index 20a45cb6f..85629be04 100644 --- a/test/pleroma/web/twitter_api/twitter_api_test.exs +++ b/test/pleroma/web/twitter_api/twitter_api_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do @@ -46,12 +46,7 @@ test "it registers a new user with empty string in bio and returns the user" do end test "it sends confirmation email if :account_activation_required is specified in instance config" do - setting = Pleroma.Config.get([:instance, :account_activation_required]) - - unless setting do - Pleroma.Config.put([:instance, :account_activation_required], true) - on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end) - end + clear_config([:instance, :account_activation_required], true) data = %{ :username => "lain", @@ -65,7 +60,7 @@ test "it sends confirmation email if :account_activation_required is specified i {:ok, user} = TwitterAPI.register_user(data) ObanHelpers.perform_all() - assert user.confirmation_pending + refute user.is_confirmed email = Pleroma.Emails.UserEmail.account_confirmation_email(user) @@ -80,13 +75,9 @@ test "it sends confirmation email if :account_activation_required is specified i end test "it sends an admin email if :account_approval_required is specified in instance config" do - admin = insert(:user, is_admin: true) - setting = Pleroma.Config.get([:instance, :account_approval_required]) + clear_config([:instance, :account_approval_required], true) - unless setting do - Pleroma.Config.put([:instance, :account_approval_required], true) - on_exit(fn -> Pleroma.Config.put([:instance, :account_approval_required], setting) end) - end + admin = insert(:user, is_admin: true) data = %{ :username => "lain", @@ -101,17 +92,26 @@ test "it sends an admin email if :account_approval_required is specified in inst {:ok, user} = TwitterAPI.register_user(data) ObanHelpers.perform_all() - assert user.approval_pending + refute user.is_approved - email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user) + user_email = Pleroma.Emails.UserEmail.approval_pending_email(user) + admin_email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user) notify_email = Pleroma.Config.get([:instance, :notify_email]) instance_name = Pleroma.Config.get([:instance, :name]) + # User approval email + Swoosh.TestAssertions.assert_email_sent( + from: {instance_name, notify_email}, + to: {user.name, user.email}, + html_body: user_email.html_body + ) + + # Admin email Swoosh.TestAssertions.assert_email_sent( from: {instance_name, notify_email}, to: {admin.name, admin.email}, - html_body: email.html_body + html_body: admin_email.html_body ) end @@ -423,10 +423,4 @@ test "it returns the error on registration problems" do assert is_binary(error) refute User.get_cached_by_nickname("lain") end - - setup do - Supervisor.terminate_child(Pleroma.Supervisor, Cachex) - Supervisor.restart_child(Pleroma.Supervisor, Cachex) - :ok - end end diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs index 60f2fb052..bdbc478c3 100644 --- a/test/pleroma/web/twitter_api/util_controller_test.exs +++ b/test/pleroma/web/twitter_api/util_controller_test.exs @@ -1,12 +1,11 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do use Pleroma.Web.ConnCase use Oban.Testing, repo: Pleroma.Repo - alias Pleroma.Config alias Pleroma.Tests.ObanHelpers alias Pleroma.User @@ -66,7 +65,7 @@ test "returns everything in :pleroma, :frontend_configurations", %{conn: conn} d } ] - Config.put(:frontend_configurations, config) + clear_config(:frontend_configurations, config) response = conn @@ -99,7 +98,7 @@ test "returns json with custom emoji with tags", %{conn: conn} do setup do: clear_config([:instance, :healthcheck]) test "returns 503 when healthcheck disabled", %{conn: conn} do - Config.put([:instance, :healthcheck], false) + clear_config([:instance, :healthcheck], false) response = conn @@ -110,7 +109,7 @@ test "returns 503 when healthcheck disabled", %{conn: conn} do end test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do - Config.put([:instance, :healthcheck], true) + clear_config([:instance, :healthcheck], true) with_mock Pleroma.Healthcheck, system_info: fn -> %Pleroma.Healthcheck{healthy: true} end do @@ -130,7 +129,7 @@ test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do end test "returns 503 when healthcheck enabled and health is false", %{conn: conn} do - Config.put([:instance, :healthcheck], true) + clear_config([:instance, :healthcheck], true) with_mock Pleroma.Healthcheck, system_info: fn -> %Pleroma.Healthcheck{healthy: false} end do @@ -164,7 +163,7 @@ test "with valid permissions and password, it disables the account", %{conn: con user = User.get_cached_by_id(user.id) - assert user.deactivated == true + refute user.is_active end test "with valid permissions and invalid password, it returns an error", %{conn: conn} do @@ -178,7 +177,7 @@ test "with valid permissions and invalid password, it returns an error", %{conn: assert response == %{"error" => "Invalid password."} user = User.get_cached_by_id(user.id) - refute user.deactivated + assert user.is_active end end @@ -397,7 +396,7 @@ test "with proper permissions, valid password and matching new password and conf assert json_response(conn, 200) == %{"status" => "success"} fetched_user = User.get_cached_by_id(user.id) - assert Pbkdf2.verify_pass("newpass", fetched_user.password_hash) == true + assert Pleroma.Password.Pbkdf2.verify_pass("newpass", fetched_user.password_hash) == true end end @@ -428,7 +427,7 @@ test "with proper permissions and valid password", %{conn: conn, user: user} do assert json_response(conn, 200) == %{"status" => "success"} user = User.get_by_id(user.id) - assert user.deactivated == true + refute user.is_active assert user.name == nil assert user.bio == "" assert user.password_hash == nil diff --git a/test/pleroma/web/uploader_controller_test.exs b/test/pleroma/web/uploader_controller_test.exs index 21e518236..fc278004e 100644 --- a/test/pleroma/web/uploader_controller_test.exs +++ b/test/pleroma/web/uploader_controller_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.UploaderControllerTest do - use Pleroma.Web.ConnCase + use Pleroma.Web.ConnCase, async: true alias Pleroma.Uploaders.Uploader describe "callback/2" do diff --git a/test/pleroma/web/views/error_view_test.exs b/test/pleroma/web/views/error_view_test.exs index 8dbbd18b4..42da8f458 100644 --- a/test/pleroma/web/views/error_view_test.exs +++ b/test/pleroma/web/views/error_view_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ErrorViewTest do diff --git a/test/pleroma/web/web_finger/web_finger_controller_test.exs b/test/pleroma/web/web_finger/web_finger_controller_test.exs index 0023f1e81..7059850bd 100644 --- a/test/pleroma/web/web_finger/web_finger_controller_test.exs +++ b/test/pleroma/web/web_finger/web_finger_controller_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do @@ -30,14 +30,24 @@ test "GET host-meta" do end test "Webfinger JRD" do - user = insert(:user) + user = + insert(:user, + ap_id: "https://hyrule.world/users/zelda", + also_known_as: ["https://mushroom.kingdom/users/toad"] + ) response = build_conn() |> put_req_header("accept", "application/jrd+json") |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost") + |> json_response(200) - assert json_response(response, 200)["subject"] == "acct:#{user.nickname}@localhost" + assert response["subject"] == "acct:#{user.nickname}@localhost" + + assert response["aliases"] == [ + "https://hyrule.world/users/zelda", + "https://mushroom.kingdom/users/toad" + ] end test "it returns 404 when user isn't found (JSON)" do @@ -51,14 +61,20 @@ test "it returns 404 when user isn't found (JSON)" do end test "Webfinger XML" do - user = insert(:user) + user = + insert(:user, + ap_id: "https://hyrule.world/users/zelda", + also_known_as: ["https://mushroom.kingdom/users/toad"] + ) response = build_conn() |> put_req_header("accept", "application/xrd+xml") |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost") + |> response(200) - assert response(response, 200) + assert response =~ "https://hyrule.world/users/zelda" + assert response =~ "https://mushroom.kingdom/users/toad" end test "it returns 404 when user isn't found (XML)" do diff --git a/test/pleroma/web/web_finger_test.exs b/test/pleroma/web/web_finger_test.exs index 96fc0bbaa..2d7b4a40b 100644 --- a/test/pleroma/web/web_finger_test.exs +++ b/test/pleroma/web/web_finger_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.WebFingerTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.Web.WebFinger import Pleroma.Factory import Tesla.Mock @@ -45,6 +45,26 @@ test "returns error for nonsensical input" do assert {:error, _} = WebFinger.finger("pleroma.social") end + test "returns error when there is no content-type header" do + Tesla.Mock.mock(fn + %{url: "http://social.heldscal.la/.well-known/host-meta"} -> + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/social.heldscal.la_host_meta") + }} + + %{ + url: + "https://social.heldscal.la/.well-known/webfinger?resource=acct:invalid_content@social.heldscal.la" + } -> + {:ok, %Tesla.Env{status: 200, body: ""}} + end) + + user = "invalid_content@social.heldscal.la" + assert {:error, {:content_type, nil}} = WebFinger.finger(user) + end + test "returns error when fails parse xml or json" do user = "invalid_content@social.heldscal.la" assert {:error, %Jason.DecodeError{}} = WebFinger.finger(user) @@ -56,12 +76,13 @@ test "returns the ActivityPub actor URI for an ActivityPub user" do {:ok, _data} = WebFinger.finger(user) end - test "returns the ActivityPub actor URI for an ActivityPub user with the ld+json mimetype" do + test "returns the ActivityPub actor URI and subscribe address for an ActivityPub user with the ld+json mimetype" do user = "kaniini@gerzilla.de" {:ok, data} = WebFinger.finger(user) assert data["ap_id"] == "https://gerzilla.de/channel/kaniini" + assert data["subscribe_address"] == "https://gerzilla.de/follow?f=&url={uri}" end test "it work for AP-only user" do @@ -112,5 +133,52 @@ test "it works with idna domains as link" do ap_id = "https://" <> to_string(:idna.encode("zetsubou.みんな")) <> "/users/lain" {:ok, _data} = WebFinger.finger(ap_id) end + + test "respects json content-type" do + Tesla.Mock.mock(fn + %{ + url: + "https://mastodon.social/.well-known/webfinger?resource=acct:emelie@mastodon.social" + } -> + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/webfinger_emelie.json"), + headers: [{"content-type", "application/jrd+json"}] + }} + + %{url: "http://mastodon.social/.well-known/host-meta"} -> + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mastodon.social_host_meta") + }} + end) + + {:ok, _data} = WebFinger.finger("emelie@mastodon.social") + end + + test "respects xml content-type" do + Tesla.Mock.mock(fn + %{ + url: "https://pawoo.net/.well-known/webfinger?resource=acct:pekorino@pawoo.net" + } -> + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.xml"), + headers: [{"content-type", "application/xrd+xml"}] + }} + + %{url: "http://pawoo.net/.well-known/host-meta"} -> + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/pawoo.net_host_meta") + }} + end) + + {:ok, _data} = WebFinger.finger("pekorino@pawoo.net") + end end end diff --git a/test/pleroma/workers/cron/digest_emails_worker_test.exs b/test/pleroma/workers/cron/digest_emails_worker_test.exs index 65887192e..b3ca6235b 100644 --- a/test/pleroma/workers/cron/digest_emails_worker_test.exs +++ b/test/pleroma/workers/cron/digest_emails_worker_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.Cron.DigestEmailsWorkerTest do @@ -14,7 +14,7 @@ defmodule Pleroma.Workers.Cron.DigestEmailsWorkerTest do setup do: clear_config([:email_notifications, :digest]) setup do - Pleroma.Config.put([:email_notifications, :digest], %{ + clear_config([:email_notifications, :digest], %{ active: true, inactivity_threshold: 7, interval: 7 diff --git a/test/pleroma/workers/cron/new_users_digest_worker_test.exs b/test/pleroma/workers/cron/new_users_digest_worker_test.exs index 129534cb1..f9ef265c2 100644 --- a/test/pleroma/workers/cron/new_users_digest_worker_test.exs +++ b/test/pleroma/workers/cron/new_users_digest_worker_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.Cron.NewUsersDigestWorkerTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true import Pleroma.Factory alias Pleroma.Tests.ObanHelpers @@ -28,7 +28,7 @@ test "it sends new users digest emails" do assert email.html_body =~ user.nickname assert email.html_body =~ user2.nickname assert email.html_body =~ "cofe" - assert email.html_body =~ "#{Pleroma.Web.Endpoint.url()}/static/logo.png" + assert email.html_body =~ "#{Pleroma.Web.Endpoint.url()}/static/logo.svg" end test "it doesn't fail when admin has no email" do diff --git a/test/pleroma/workers/purge_expired_activity_test.exs b/test/pleroma/workers/purge_expired_activity_test.exs index b5938776d..98f30f61f 100644 --- a/test/pleroma/workers/purge_expired_activity_test.exs +++ b/test/pleroma/workers/purge_expired_activity_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.PurgeExpiredActivityTest do diff --git a/test/pleroma/workers/purge_expired_filter_test.exs b/test/pleroma/workers/purge_expired_filter_test.exs new file mode 100644 index 000000000..d10586be9 --- /dev/null +++ b/test/pleroma/workers/purge_expired_filter_test.exs @@ -0,0 +1,30 @@ +defmodule Pleroma.Workers.PurgeExpiredFilterTest do + use Pleroma.DataCase, async: true + use Oban.Testing, repo: Repo + + import Pleroma.Factory + + test "purges expired filter" do + %{id: user_id} = insert(:user) + + {:ok, %{id: id}} = + Pleroma.Filter.create(%{ + user_id: user_id, + phrase: "cofe", + context: ["home"], + expires_in: 600 + }) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredFilter, + args: %{filter_id: id} + ) + + assert {:ok, %{id: ^id}} = + perform_job(Pleroma.Workers.PurgeExpiredFilter, %{ + filter_id: id + }) + + assert Repo.aggregate(Pleroma.Filter, :count, :id) == 0 + end +end diff --git a/test/pleroma/workers/purge_expired_token_test.exs b/test/pleroma/workers/purge_expired_token_test.exs index fb7708c3f..00cbd40cd 100644 --- a/test/pleroma/workers/purge_expired_token_test.exs +++ b/test/pleroma/workers/purge_expired_token_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.PurgeExpiredTokenTest do diff --git a/test/pleroma/workers/scheduled_activity_worker_test.exs b/test/pleroma/workers/scheduled_activity_worker_test.exs index f3eddf7b1..5558d5b5f 100644 --- a/test/pleroma/workers/scheduled_activity_worker_test.exs +++ b/test/pleroma/workers/scheduled_activity_worker_test.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.ScheduledActivityWorkerTest do @@ -11,10 +11,9 @@ defmodule Pleroma.Workers.ScheduledActivityWorkerTest do import Pleroma.Factory import ExUnit.CaptureLog - setup do: clear_config([ScheduledActivity, :enabled]) + setup do: clear_config([ScheduledActivity, :enabled], true) test "creates a status from the scheduled activity" do - Pleroma.Config.put([ScheduledActivity, :enabled], true) user = insert(:user) naive_datetime = @@ -32,18 +31,22 @@ test "creates a status from the scheduled activity" do params: %{status: "hi"} ) - ScheduledActivityWorker.perform(%Oban.Job{args: %{"activity_id" => scheduled_activity.id}}) + {:ok, %{id: activity_id}} = + ScheduledActivityWorker.perform(%Oban.Job{args: %{"activity_id" => scheduled_activity.id}}) refute Repo.get(ScheduledActivity, scheduled_activity.id) - activity = Repo.all(Pleroma.Activity) |> Enum.find(&(&1.actor == user.ap_id)) - assert Pleroma.Object.normalize(activity).data["content"] == "hi" + + object = + Pleroma.Activity + |> Repo.get(activity_id) + |> Pleroma.Object.normalize() + + assert object.data["content"] == "hi" end - test "adds log message if ScheduledActivity isn't find" do - Pleroma.Config.put([ScheduledActivity, :enabled], true) - + test "error message for non-existent scheduled activity" do assert capture_log([level: :error], fn -> ScheduledActivityWorker.perform(%Oban.Job{args: %{"activity_id" => 42}}) - end) =~ "Couldn't find scheduled activity" + end) =~ "Couldn't find scheduled activity: 42" end end diff --git a/test/pleroma/xml_builder_test.exs b/test/pleroma/xml_builder_test.exs index 059384c34..9aae32cdc 100644 --- a/test/pleroma/xml_builder_test.exs +++ b/test/pleroma/xml_builder_test.exs @@ -1,9 +1,9 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.XmlBuilderTest do - use Pleroma.DataCase + use Pleroma.DataCase, async: true alias Pleroma.XmlBuilder test "Build a basic xml string from a tuple" do diff --git a/test/support/api_spec_helpers.ex b/test/support/api_spec_helpers.ex index 46388f92c..36d6a8b81 100644 --- a/test/support/api_spec_helpers.ex +++ b/test/support/api_spec_helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Tests.ApiSpecHelpers do diff --git a/test/support/builders/user_builder.ex b/test/support/builders/user_builder.ex index 0c687c029..6bccbb35a 100644 --- a/test/support/builders/user_builder.ex +++ b/test/support/builders/user_builder.ex @@ -7,7 +7,7 @@ def build(data \\ %{}) do email: "test@example.org", name: "Test Name", nickname: "testname", - password_hash: Pbkdf2.hash_pwd_salt("test"), + password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"), bio: "A tester.", ap_id: "some id", last_digest_emailed_at: NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second), diff --git a/test/support/cachex_proxy.ex b/test/support/cachex_proxy.ex new file mode 100644 index 000000000..de1f1c766 --- /dev/null +++ b/test/support/cachex_proxy.ex @@ -0,0 +1,40 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.CachexProxy do + @behaviour Pleroma.Caching + + @impl true + defdelegate get!(cache, key), to: Cachex + + @impl true + defdelegate stream!(cache, key), to: Cachex + + @impl true + defdelegate put(cache, key, value, options), to: Cachex + + @impl true + defdelegate put(cache, key, value), to: Cachex + + @impl true + defdelegate get_and_update(cache, key, func), to: Cachex + + @impl true + defdelegate get(cache, key), to: Cachex + + @impl true + defdelegate fetch!(cache, key, func), to: Cachex + + @impl true + defdelegate expire_at(cache, str, num), to: Cachex + + @impl true + defdelegate exists?(cache, key), to: Cachex + + @impl true + defdelegate del(cache, key), to: Cachex + + @impl true + defdelegate execute!(cache, func), to: Cachex +end diff --git a/test/support/captcha/mock.ex b/test/support/captcha/mock.ex index 2ed2ba3b4..175ade131 100644 --- a/test/support/captcha/mock.ex +++ b/test/support/captcha/mock.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Captcha.Mock do diff --git a/test/support/channel_case.ex b/test/support/channel_case.ex index 114184a9f..1fbf6f100 100644 --- a/test/support/channel_case.ex +++ b/test/support/channel_case.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ChannelCase do @@ -30,13 +30,5 @@ defmodule Pleroma.Web.ChannelCase do end end - setup tags do - :ok = Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo) - - unless tags[:async] do - Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, {:shared, self()}) - end - - :ok - end + setup tags, do: Pleroma.DataCase.setup_multi_process_mode(tags) end diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 47cb65a80..deee98599 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ConnCase do @@ -19,6 +19,8 @@ defmodule Pleroma.Web.ConnCase do use ExUnit.CaseTemplate + alias Pleroma.DataCase + using do quote do # Import conveniences for testing with connections @@ -65,13 +67,11 @@ defp empty_json_response(conn) do end defp json_response_and_validate_schema( - %{ - private: %{ - open_api_spex: %{operation_id: op_id, operation_lookup: lookup, spec: spec} - } - } = conn, + %{private: %{operation_id: op_id}} = conn, status ) do + {spec, lookup} = OpenApiSpex.Plug.PutApiSpec.get_spec_and_operation_lookup(conn) + content_type = conn |> Plug.Conn.get_resp_header("content-type") @@ -116,21 +116,11 @@ defp json_response_and_validate_schema(conn, _status) do end setup tags do - Cachex.clear(:user_cache) - Cachex.clear(:object_cache) - :ok = Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo) + DataCase.setup_multi_process_mode(tags) + DataCase.setup_streamer(tags) + DataCase.stub_pipeline() - unless tags[:async] do - Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, {:shared, self()}) - end - - if tags[:needs_streamer] do - start_supervised(%{ - id: Pleroma.Web.Streamer.registry(), - start: - {Registry, :start_link, [[keys: :duplicate, name: Pleroma.Web.Streamer.registry()]]} - }) - end + Mox.verify_on_exit!() {:ok, conn: Phoenix.ConnTest.build_conn()} end diff --git a/test/support/data_case.ex b/test/support/data_case.ex index d5456521c..0ee2aa4a2 100644 --- a/test/support/data_case.ex +++ b/test/support/data_case.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.DataCase do @@ -18,6 +18,8 @@ defmodule Pleroma.DataCase do use ExUnit.CaseTemplate + import Pleroma.Tests.Helpers, only: [clear_config: 2] + using do quote do alias Pleroma.Repo @@ -45,15 +47,41 @@ defp oauth_access(scopes, opts \\ []) do end end - setup tags do - Cachex.clear(:user_cache) - Cachex.clear(:object_cache) + def clear_cachex do + Pleroma.Supervisor + |> Supervisor.which_children() + |> Enum.each(fn + {name, _, _, [Cachex]} -> + name + |> to_string + |> String.trim_leading("cachex_") + |> Kernel.<>("_cache") + |> String.to_existing_atom() + |> Cachex.clear() + + _ -> + nil + end) + end + + def setup_multi_process_mode(tags) do :ok = Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo) - unless tags[:async] do + if tags[:async] do + Mox.stub_with(Pleroma.CachexMock, Pleroma.NullCache) + Mox.set_mox_private() + else Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, {:shared, self()}) + + Mox.set_mox_global() + Mox.stub_with(Pleroma.CachexMock, Pleroma.CachexProxy) + clear_cachex() end + :ok + end + + def setup_streamer(tags) do if tags[:needs_streamer] do start_supervised(%{ id: Pleroma.Web.Streamer.registry(), @@ -65,18 +93,35 @@ defp oauth_access(scopes, opts \\ []) do :ok end + setup tags do + setup_multi_process_mode(tags) + setup_streamer(tags) + stub_pipeline() + + Mox.verify_on_exit!() + + :ok + end + + def stub_pipeline do + Mox.stub_with(Pleroma.Web.ActivityPub.SideEffectsMock, Pleroma.Web.ActivityPub.SideEffects) + + Mox.stub_with( + Pleroma.Web.ActivityPub.ObjectValidatorMock, + Pleroma.Web.ActivityPub.ObjectValidator + ) + + Mox.stub_with(Pleroma.Web.ActivityPub.MRFMock, Pleroma.Web.ActivityPub.MRF) + Mox.stub_with(Pleroma.Web.ActivityPub.ActivityPubMock, Pleroma.Web.ActivityPub.ActivityPub) + Mox.stub_with(Pleroma.Web.FederatorMock, Pleroma.Web.Federator) + Mox.stub_with(Pleroma.ConfigMock, Pleroma.Config) + end + def ensure_local_uploader(context) do - test_uploader = Map.get(context, :uploader, Pleroma.Uploaders.Local) - uploader = Pleroma.Config.get([Pleroma.Upload, :uploader]) - filters = Pleroma.Config.get([Pleroma.Upload, :filters]) + test_uploader = Map.get(context, :uploader) || Pleroma.Uploaders.Local - Pleroma.Config.put([Pleroma.Upload, :uploader], test_uploader) - Pleroma.Config.put([Pleroma.Upload, :filters], []) - - on_exit(fn -> - Pleroma.Config.put([Pleroma.Upload, :uploader], uploader) - Pleroma.Config.put([Pleroma.Upload, :filters], filters) - end) + clear_config([Pleroma.Upload, :uploader], test_uploader) + clear_config([Pleroma.Upload, :filters], []) :ok end diff --git a/test/support/factory.ex b/test/support/factory.ex index 8eb07dc3c..5c4e65c81 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -1,9 +1,12 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Factory do use ExMachina.Ecto, repo: Pleroma.Repo + + require Pleroma.Constants + alias Pleroma.Object alias Pleroma.User @@ -29,7 +32,7 @@ def user_factory(attrs \\ %{}) do name: sequence(:name, &"Test テスト User #{&1}"), email: sequence(:email, &"user#{&1}@example.com"), nickname: sequence(:nickname, &"nick#{&1}"), - password_hash: Pbkdf2.hash_pwd_salt("test"), + password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"), bio: sequence(:bio, &"Tester Number #{&1}"), is_discoverable: true, last_digest_emailed_at: NaiveDateTime.utc_now(), @@ -41,23 +44,27 @@ def user_factory(attrs \\ %{}) do urls = if attrs[:local] == false do - base_domain = Enum.random(["domain1.com", "domain2.com", "domain3.com"]) + base_domain = attrs[:domain] || Enum.random(["domain1.com", "domain2.com", "domain3.com"]) ap_id = "https://#{base_domain}/users/#{user.nickname}" %{ ap_id: ap_id, follower_address: ap_id <> "/followers", - following_address: ap_id <> "/following" + following_address: ap_id <> "/following", + featured_address: ap_id <> "/collections/featured" } else %{ ap_id: User.ap_id(user), follower_address: User.ap_followers(user), - following_address: User.ap_following(user) + following_address: User.ap_following(user), + featured_address: User.ap_featured_collection(user) } end + attrs = Map.delete(attrs, :domain) + user |> Map.put(:raw_bio, user.bio) |> Map.merge(urls) @@ -104,6 +111,37 @@ def note_factory(attrs \\ %{}) do } end + def attachment_note_factory(attrs \\ %{}) do + user = attrs[:user] || insert(:user) + {length, attrs} = Map.pop(attrs, :length, 1) + + data = %{ + "attachment" => + Stream.repeatedly(fn -> attachment_data(user.ap_id, attrs[:href]) end) + |> Enum.take(length) + } + + build(:note, Map.put(attrs, :data, data)) + end + + defp attachment_data(ap_id, href) do + href = href || sequence(:href, &"#{Pleroma.Web.Endpoint.url()}/media/#{&1}.jpg") + + %{ + "url" => [ + %{ + "href" => href, + "type" => "Link", + "mediaType" => "image/jpeg" + } + ], + "name" => "some name", + "type" => "Document", + "actor" => ap_id, + "mediaType" => "image/jpeg" + } + end + def audio_factory(attrs \\ %{}) do text = sequence(:text, &"lain radio episode #{&1}") @@ -190,6 +228,45 @@ def direct_note_activity_factory do } end + def add_activity_factory(attrs \\ %{}) do + featured_collection_activity(attrs, "Add") + end + + def remove_activity_factor(attrs \\ %{}) do + featured_collection_activity(attrs, "Remove") + end + + defp featured_collection_activity(attrs, type) do + user = attrs[:user] || insert(:user) + note = attrs[:note] || insert(:note, user: user) + + data_attrs = + attrs + |> Map.get(:data_attrs, %{}) + |> Map.put(:type, type) + + attrs = Map.drop(attrs, [:user, :note, :data_attrs]) + + data = + %{ + "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), + "target" => user.featured_address, + "object" => note.data["object"], + "actor" => note.data["actor"], + "type" => "Add", + "to" => [Pleroma.Constants.as_public()], + "cc" => [user.follower_address] + } + |> Map.merge(data_attrs) + + %Pleroma.Activity{ + data: data, + actor: data["actor"], + recipients: data["to"] + } + |> Map.merge(attrs) + end + def note_activity_factory(attrs \\ %{}) do user = attrs[:user] || insert(:user) note = attrs[:note] || insert(:note, user: user) @@ -259,7 +336,7 @@ def announce_activity_factory(attrs \\ %{}) do def like_activity_factory(attrs \\ %{}) do note_activity = attrs[:note_activity] || insert(:note_activity) - object = Object.normalize(note_activity) + object = Object.normalize(note_activity, fetch: false) user = insert(:user) data = @@ -455,7 +532,8 @@ def filter_factory do %Pleroma.Filter{ user: build(:user), filter_id: sequence(:filter_id, & &1), - phrase: "cofe" + phrase: "cofe", + context: ["home"] } end end diff --git a/test/support/helpers.ex b/test/support/helpers.ex index 224034521..856a6a376 100644 --- a/test/support/helpers.ex +++ b/test/support/helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Tests.Helpers do @@ -8,6 +8,8 @@ defmodule Pleroma.Tests.Helpers do """ alias Pleroma.Config + require Logger + defmacro clear_config(config_path) do quote do clear_config(unquote(config_path)) do @@ -18,6 +20,7 @@ defmacro clear_config(config_path) do defmacro clear_config(config_path, do: yield) do quote do initial_setting = Config.fetch(unquote(config_path)) + unquote(yield) on_exit(fn -> @@ -35,6 +38,15 @@ defmacro clear_config(config_path, do: yield) do end defmacro clear_config(config_path, temp_setting) do + # NOTE: `clear_config([section, key], value)` != `clear_config([section], key: value)` (!) + # Displaying a warning to prevent unintentional clearing of all but one keys in section + if Keyword.keyword?(temp_setting) and length(temp_setting) == 1 do + Logger.warn( + "Please change to `clear_config([section]); clear_config([section, key], value)`: " <> + "#{inspect(config_path)}, #{inspect(temp_setting)}" + ) + end + quote do clear_config(unquote(config_path)) do Config.put(unquote(config_path), unquote(temp_setting)) @@ -55,6 +67,14 @@ defmacro __using__(_opts) do clear_config: 2 ] + def time_travel(entity, seconds) do + new_time = NaiveDateTime.add(entity.inserted_at, seconds) + + entity + |> Ecto.Changeset.change(%{inserted_at: new_time, updated_at: new_time}) + |> Pleroma.Repo.update() + end + def to_datetime(%NaiveDateTime{} = naive_datetime) do naive_datetime |> DateTime.from_naive!("Etc/UTC") diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index 93464ebff..8807c2d14 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule HttpRequestMock do @@ -89,6 +89,18 @@ def get("https://mastodon.sdf.org/users/rinpatch", _, _, _) do }} end + def get("https://mastodon.sdf.org/users/rinpatch/collections/featured", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: + File.read!("test/fixtures/users_mock/masto_featured.json") + |> String.replace("{{domain}}", "mastodon.sdf.org") + |> String.replace("{{nickname}}", "rinpatch"), + headers: [{"content-type", "application/activity+json"}] + }} + end + def get("https://patch.cx/objects/tesla_mock/poll_attachment", _, _, _) do {:ok, %Tesla.Env{ @@ -122,7 +134,7 @@ def get( %Tesla.Env{ status: 200, body: File.read!("test/fixtures/tesla_mock/mike@osada.macgirvin.com.json"), - headers: activitypub_object_headers() + headers: [{"content-type", "application/jrd+json"}] }} end @@ -187,7 +199,8 @@ def get( {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/lain_squeet.me_webfinger.xml") + body: File.read!("test/fixtures/tesla_mock/lain_squeet.me_webfinger.xml"), + headers: [{"content-type", "application/xrd+xml"}] }} end @@ -275,6 +288,15 @@ def get("https://peertube.moe/accounts/7even", _, _, _) do }} end + def get("https://peertube.stream/accounts/createurs", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/peertube/actor-person.json"), + headers: activitypub_object_headers() + }} + end + def get("https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3", _, _, _) do {:ok, %Tesla.Env{ @@ -517,22 +539,6 @@ def get( }} 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{ @@ -777,7 +783,8 @@ def get( {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/shp@social.heldscal.la.xml") + body: File.read!("test/fixtures/tesla_mock/shp@social.heldscal.la.xml"), + headers: [{"content-type", "application/xrd+xml"}] }} end @@ -787,7 +794,7 @@ def get( _, [{"accept", "application/xrd+xml,application/jrd+json"}] ) do - {:ok, %Tesla.Env{status: 200, body: ""}} + {:ok, %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/jrd+json"}]}} end def get("http://framatube.org/.well-known/host-meta", _, _, _) do @@ -807,7 +814,7 @@ def get( {:ok, %Tesla.Env{ status: 200, - headers: [{"content-type", "application/json"}], + headers: [{"content-type", "application/jrd+json"}], body: File.read!("test/fixtures/tesla_mock/framasoft@framatube.org.json") }} end @@ -867,7 +874,7 @@ def get( {:ok, %Tesla.Env{ status: 200, - headers: [{"content-type", "application/json"}], + headers: [{"content-type", "application/jrd+json"}], body: File.read!("test/fixtures/tesla_mock/kaniini@gerzilla.de.json") }} end @@ -910,6 +917,18 @@ def get("https://mastodon.social/users/lambadalambda", _, _, _) do }} end + def get("https://mastodon.social/users/lambadalambda/collections/featured", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: + File.read!("test/fixtures/users_mock/masto_featured.json") + |> String.replace("{{domain}}", "mastodon.social") + |> String.replace("{{nickname}}", "lambadalambda"), + headers: activitypub_object_headers() + }} + end + def get("https://apfed.club/channel/indio", _, _, _) do {:ok, %Tesla.Env{ @@ -1065,7 +1084,8 @@ def get( {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/lain.xml") + body: File.read!("test/fixtures/lain.xml"), + headers: [{"content-type", "application/xrd+xml"}] }} end @@ -1078,7 +1098,16 @@ def get( {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/lain.xml") + body: File.read!("test/fixtures/lain.xml"), + headers: [{"content-type", "application/xrd+xml"}] + }} + end + + def get("http://zetsubou.xn--q9jyb4c/.well-known/host-meta", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml") }} end @@ -1144,7 +1173,8 @@ def get("https://mstdn.jp/.well-known/webfinger?resource=acct:kpherox@mstdn.jp", {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/kpherox@mstdn.jp.xml") + body: File.read!("test/fixtures/tesla_mock/kpherox@mstdn.jp.xml"), + headers: [{"content-type", "application/xrd+xml"}] }} end @@ -1272,6 +1302,15 @@ def get("https://osada.macgirvin.com/", _, "", [{"accept", "text/html"}]) do }} end + def get("https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f", _, _, _) do + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/emoji-in-summary.json"), + headers: activitypub_object_headers() + }} + end + def get(url, query, body, headers) do {:error, "Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{ diff --git a/test/support/mocks.ex b/test/support/mocks.ex new file mode 100644 index 000000000..fd8f825b3 --- /dev/null +++ b/test/support/mocks.ex @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +Mox.defmock(Pleroma.CachexMock, for: Pleroma.Caching) + +Mox.defmock(Pleroma.Web.ActivityPub.ObjectValidatorMock, + for: Pleroma.Web.ActivityPub.ObjectValidator.Validating +) + +Mox.defmock(Pleroma.Web.ActivityPub.MRFMock, + for: Pleroma.Web.ActivityPub.MRF.PipelineFiltering +) + +Mox.defmock(Pleroma.Web.ActivityPub.ActivityPubMock, + for: [ + Pleroma.Web.ActivityPub.ActivityPub.Persisting, + Pleroma.Web.ActivityPub.ActivityPub.Streaming + ] +) + +Mox.defmock(Pleroma.Web.ActivityPub.SideEffectsMock, + for: Pleroma.Web.ActivityPub.SideEffects.Handling +) + +Mox.defmock(Pleroma.Web.FederatorMock, for: Pleroma.Web.Federator.Publishing) + +Mox.defmock(Pleroma.ConfigMock, for: Pleroma.Config.Getting) + +Mox.defmock(Pleroma.LoggerMock, for: Pleroma.Logging) diff --git a/test/support/mrf_module_mock.ex b/test/support/mrf_module_mock.ex index 028ea542a..4dfdeb3b4 100644 --- a/test/support/mrf_module_mock.ex +++ b/test/support/mrf_module_mock.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule MRFModuleMock do diff --git a/test/support/null_cache.ex b/test/support/null_cache.ex new file mode 100644 index 000000000..47c10ebb6 --- /dev/null +++ b/test/support/null_cache.ex @@ -0,0 +1,49 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.NullCache do + @moduledoc """ + A module simulating a permanently empty cache. + """ + @behaviour Pleroma.Caching + + @impl true + def get!(_, _), do: nil + + @impl true + def put(_, _, _, _ \\ nil), do: {:ok, true} + + @impl true + def stream!(_, _), do: [] + + @impl true + def get(_, _), do: {:ok, nil} + + @impl true + def fetch!(_, key, func) do + case func.(key) do + {_, res} -> res + res -> res + end + end + + @impl true + def get_and_update(_, _, func) do + func.(nil) + end + + @impl true + def expire_at(_, _, _), do: {:ok, true} + + @impl true + def exists?(_, _), do: {:ok, false} + + @impl true + def execute!(_, func) do + func.(:nothing) + end + + @impl true + def del(_, _), do: {:ok, true} +end diff --git a/test/support/oban_helpers.ex b/test/support/oban_helpers.ex index 2468f66dc..9b6e5256e 100644 --- a/test/support/oban_helpers.ex +++ b/test/support/oban_helpers.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Tests.ObanHelpers do diff --git a/test/support/websocket_client.ex b/test/support/websocket_client.ex index 8c9d4b2b4..34b955474 100644 --- a/test/support/websocket_client.ex +++ b/test/support/websocket_client.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Integration.WebsocketClient do diff --git a/test/test_helper.exs b/test/test_helper.exs index ee880e226..0c9783076 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only os_exclude = if :os.type() == {:unix, :darwin}, do: [skip_on_mac: true], else: []