diff --git a/.credo.exs b/.credo.exs index 46d45d015..b85898af3 100644 --- a/.credo.exs +++ b/.credo.exs @@ -25,7 +25,7 @@ # # If you create your own checks, you must specify the source files for # them here, so they can be loaded by Credo before running the analysis. - requires: [], + requires: ["./test/credo/check/consistency/file_location.ex"], # # Credo automatically checks for updates, like e.g. Hex does. # You can disable this behaviour below: @@ -71,7 +71,6 @@ # set this value to 0 (zero). {Credo.Check.Design.TagTODO, exit_status: 0}, {Credo.Check.Design.TagFIXME, exit_status: 0}, - {Credo.Check.Readability.FunctionNames}, {Credo.Check.Readability.LargeNumbers}, {Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 100}, @@ -91,7 +90,6 @@ {Credo.Check.Readability.VariableNames}, {Credo.Check.Readability.Semicolons}, {Credo.Check.Readability.SpaceAfterCommas}, - {Credo.Check.Refactor.DoubleBooleanNegation}, {Credo.Check.Refactor.CondStatements}, {Credo.Check.Refactor.CyclomaticComplexity}, @@ -102,7 +100,6 @@ {Credo.Check.Refactor.Nesting}, {Credo.Check.Refactor.PipeChainStart}, {Credo.Check.Refactor.UnlessWithElse}, - {Credo.Check.Warning.BoolOperationOnSameValues}, {Credo.Check.Warning.IExPry}, {Credo.Check.Warning.IoInspect}, @@ -131,6 +128,7 @@ # Custom checks can be created using `mix credo.gen.check`. # + {Credo.Check.Consistency.FileLocation} ] } ] diff --git a/.gitignore b/.gitignore index 599b52b9e..6ae21e914 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,8 @@ erl_crash.dump # variables. /config/*.secret.exs /config/generated_config.exs +/config/*.env + # Database setup file, some may forget to delete it /config/setup_db.psql diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dc953a929..fd0c5c8d4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,6 +25,8 @@ before_script: - apt-get update && apt-get install -y cmake - mix local.hex --force - mix local.rebar --force + - apt-get -qq update + - apt-get install -y libmagic-dev build: stage: build @@ -59,7 +61,7 @@ unit-testing: alias: postgres 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 + - apt-get update && apt-get install -y libimage-exiftool-perl ffmpeg - mix deps.get - mix ecto.create - mix ecto.migrate @@ -93,7 +95,7 @@ unit-testing-rum: <<: *global_variables RUM_ENABLED: "true" script: - - apt-get update && apt-get install -y libimage-exiftool-perl + - apt-get update && apt-get install -y libimage-exiftool-perl ffmpeg - mix deps.get - mix ecto.create - mix ecto.migrate @@ -196,7 +198,7 @@ amd64: variables: &release-variables MIX_ENV: prod before_script: &before-release - - apt-get update && apt-get install -y cmake + - apt-get update && apt-get install -y cmake libmagic-dev - echo "import Mix.Config" > config/prod.secret.exs - mix local.hex --force - mix local.rebar --force @@ -215,7 +217,7 @@ amd64-musl: cache: *release-cache variables: *release-variables before_script: &before-release-musl - - apk add git gcc g++ musl-dev make cmake + - apk add git gcc g++ musl-dev make cmake file-dev - echo "import Mix.Config" > config/prod.secret.exs - mix local.hex --force - mix local.rebar --force diff --git a/CHANGELOG.md b/CHANGELOG.md index 770bd13f0..f0b90ff40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,20 +5,47 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased +### Added +- 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 option for force-unfollowing relays +- Media preview proxy (requires `ffmpeg` and `ImageMagick` to be installed and media proxy to be enabled; see `:media_preview_proxy` config for more details). +- Pleroma API: Importing the mutes users from CSV files. +- Experimental websocket-based federation between Pleroma instances. +- Support pagination of blocks and mutes +- App metrics: ability to restrict access to specified IP whitelist. +- 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` + ### Changed +- **Breaking** Requires `libmagic` (or `file`) to guess file types. +- **Breaking:** Pleroma Admin API: emoji packs and files routes changed. +- **Breaking:** Sensitive/NSFW statuses no longer disable link previews. +- **Breaking:** App metrics endpoint (`/api/pleroma/app_metrics`) is disabled by default, check `docs/API/prometheus.md` on enabling and configuring. +- Search: Users are now findable by their urls. - Renamed `:await_up_timeout` in `:connections_pool` namespace to `:connect_timeout`, old name is deprecated. - Renamed `:timeout` in `pools` namespace to `:recv_timeout`, old name is deprecated. - The `discoverable` field in the `User` struct will now add a NOINDEX metatag to profile pages when false. - Users with the `discoverable` field set to false will not show up in searches. - Minimum lifetime for ephmeral activities changed to 10 minutes and made configurable (`:min_lifetime` option). +- Introduced optional dependencies on `ffmpeg`, `ImageMagick`, `exiftool` software packages. Please refer to `docs/installation/optional/media_graphics_packages.md`. +- Polls now always return a `voters_count`, even if they are single-choice + +
+ API Changes -### Added -- Media preview proxy (requires media proxy be enabled; see `:media_preview_proxy` config for more details). - Pleroma API: Importing the mutes users from CSV files. -- Experimental websocket-based federation between Pleroma instances. - Admin API: Importing emoji from a zip file -- User and conversation mutes can now auto-expire if `expires_in` parameter was given while adding the mute. +- Pleroma API: Pagination for remote/local packs and emoji. +- Admin API: (`GET /api/pleroma/admin/users`) added filters user by `unconfirmed` status +- Admin API: (`GET /api/pleroma/admin/users`) added filters user by `actor_type` +- 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. + +
### Removed @@ -29,7 +56,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). switched to a new configuration mechanism, however it was not officially removed until now. ### Fixed + +- Add documented-but-missing chat pagination. - Allow sending out emails again. +- Allow sending chat messages to yourself. +- Fix remote users with a whitespace name. +- OStatus / static FE endpoints: fixed inaccessibility for anonymous users on non-federating instances, switched to handling per `:restrict_unauthenticated` setting. +- 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 + +## Unreleased (Patch) + +### Changed +- API: Empty parameter values for integer parameters are now ignored in non-strict validaton mode. ## [2.1.2] - 2020-09-17 diff --git a/config/benchmark.exs b/config/benchmark.exs index e867253eb..5567ff26e 100644 --- a/config/benchmark.exs +++ b/config/benchmark.exs @@ -59,8 +59,6 @@ "BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4", private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA" -config :web_push_encryption, :http_client, Pleroma.Web.WebPushHttpClientMock - config :pleroma, Pleroma.ScheduledActivity, daily_user_limit: 2, total_user_limit: 3, diff --git a/config/config.exs b/config/config.exs index d3e42fe62..c77529e87 100644 --- a/config/config.exs +++ b/config/config.exs @@ -123,7 +123,6 @@ # Configures the endpoint config :pleroma, Pleroma.Web.Endpoint, - instrumenters: [Pleroma.Web.Endpoint.Instrumenter], url: [host: "localhost"], http: [ ip: {127, 0, 0, 1}, @@ -143,7 +142,7 @@ secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl", signing_salt: "CqaoopA2", render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)], - pubsub: [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2], + pubsub_server: Pleroma.PubSub, secure_cookie_flag: true, extra_cookie_attrs: [ "SameSite=Lax" @@ -235,6 +234,7 @@ "text/bbcode" ], autofollowed_nicknames: [], + autofollowing_nicknames: [], max_pinned_statuses: 1, attachment_links: false, max_report_comment_size: 1000, @@ -551,6 +551,7 @@ queues: [ activity_expiration: 10, token_expiration: 5, + backup: 1, federator_incoming: 50, federator_outgoing: 50, ingestion_queue: 50, @@ -637,7 +638,12 @@ config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false -config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics" +config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, + enabled: false, + auth: false, + ip_whitelist: [], + path: "/api/pleroma/app_metrics", + format: :text config :pleroma, Pleroma.ScheduledActivity, daily_user_limit: 25, @@ -678,7 +684,18 @@ config :pleroma, Pleroma.Workers.PurgeExpiredActivity, enabled: true, min_lifetime: 600 -config :pleroma, Pleroma.Plugs.RemoteIp, enabled: true +config :pleroma, Pleroma.Web.Plugs.RemoteIp, + enabled: true, + headers: ["x-forwarded-for"], + proxies: [], + reserved: [ + "127.0.0.0/8", + "::1/128", + "fc00::/7", + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16" + ] config :pleroma, :static_fe, enabled: false @@ -792,6 +809,8 @@ timeout: 300_000 ] +config :pleroma, :majic_pool, size: 2 + private_instance? = :if_instance_is_private config :pleroma, :restrict_unauthenticated, @@ -810,7 +829,7 @@ config :ex_aws, http_client: Pleroma.HTTP.ExAws -config :web_push_encryption, http_client: Pleroma.HTTP +config :web_push_encryption, http_client: Pleroma.HTTP.WebPush config :pleroma, :instances_favicons, enabled: false @@ -818,6 +837,11 @@ config :pleroma, Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.PleromaAuthenticator +config :pleroma, Pleroma.User.Backup, + purge_after_days: 30, + limit_days: 7, + dir: nil + # 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 ac3dfbb2b..0b651696b 100644 --- a/config/description.exs +++ b/config/description.exs @@ -44,11 +44,13 @@ }, %{ key: "git", + label: "Git Repository URL", type: :string, description: "URL of the git repository of the frontend" }, %{ key: "build_url", + label: "Build URL", type: :string, description: "Either an url to a zip file containing the frontend or a template to build it by inserting the `ref`. The string `${ref}` will be replaced by the configured `ref`.", @@ -56,6 +58,7 @@ }, %{ key: "build_dir", + label: "Build directory", type: :string, description: "The directory inside the zip file " } @@ -826,13 +829,13 @@ key: :autofollowed_nicknames, type: {:list, :string}, description: - "Set to nicknames of (local) users that every new user should automatically follow", - suggestions: [ - "lain", - "kaniini", - "lanodan", - "rinpatch" - ] + "Set to nicknames of (local) users that every new user should automatically follow" + }, + %{ + key: :autofollowing_nicknames, + type: {:list, :string}, + description: + "Set to nicknames of (local) users that automatically follows every newly registered user" }, %{ key: :attachment_links, @@ -1754,28 +1757,37 @@ related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy", label: "MRF Keyword", type: :group, - description: "Reject or Word-Replace messages with a keyword or regex", + description: + "Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).", children: [ %{ key: :reject, type: {:list, :string}, - description: - "A list of patterns which result in message being rejected. Each pattern can be a string or a regular expression.", + description: """ + A list of patterns which result in message being rejected. + + Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. + """, suggestions: ["foo", ~r/foo/iu] }, %{ key: :federated_timeline_removal, type: {:list, :string}, - description: - "A list of patterns which result in message being removed from federated timelines (a.k.a unlisted). Each pattern can be a string or a regular expression.", + description: """ + A list of patterns which result in message being removed from federated timelines (a.k.a unlisted). + + Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. + """, suggestions: ["foo", ~r/foo/iu] }, %{ key: :replace, type: {:list, :tuple}, - description: - "A list of tuples containing {pattern, replacement}. Each pattern can be a string or a regular expression.", - suggestions: [{"foo", "bar"}, {~r/foo/iu, "bar"}] + description: """ + **Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. + + **Replacement**: a string. Leaving the field empty is permitted. + """ } ] }, @@ -2285,6 +2297,12 @@ description: "Activity expiration queue", suggestions: [10] }, + %{ + key: :backup, + type: :integer, + description: "Backup queue", + suggestions: [1] + }, %{ key: :attachments_cleanup, type: :integer, @@ -3247,10 +3265,10 @@ }, %{ group: :pleroma, - key: Pleroma.Plugs.RemoteIp, + key: Pleroma.Web.Plugs.RemoteIp, type: :group, description: """ - `Pleroma.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. + `Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. **If your instance is not behind at least one reverse proxy, you should not enable this plug.** """, children: [ @@ -3262,20 +3280,22 @@ %{ key: :headers, type: {:list, :string}, - description: - "A list of strings naming the `req_headers` to use when deriving the `remote_ip`. Order does not matter. Default: `~w[forwarded x-forwarded-for x-client-ip x-real-ip]`." + description: """ + A list of strings naming the HTTP headers to use when deriving the true client IP. Default: `["x-forwarded-for"]`. + """ }, %{ key: :proxies, type: {:list, :string}, description: - "A list of strings in [CIDR](https://en.wikipedia.org/wiki/CIDR) notation specifying the IPs of known proxies. Default: `[]`." + "A list of upstream proxy IP subnets in CIDR notation from which we will parse the content of `headers`. Defaults to `[]`. IPv4 entries without a bitmask will be assumed to be /32 and IPv6 /128." }, %{ key: :reserved, type: {:list, :string}, - description: - "Defaults to [localhost](https://en.wikipedia.org/wiki/Localhost) and [private network](https://en.wikipedia.org/wiki/Private_network)." + description: """ + A list of reserved IP subnets in CIDR notation which should be ignored if found in `headers`. Defaults to `["127.0.0.0/8", "::1/128", "fc00::/7", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]` + """ } ] }, @@ -3681,9 +3701,7 @@ type: :map, description: "A map containing available frontends and parameters for their installation.", - children: [ - frontend_options - ] + children: frontend_options } ] }, @@ -3705,5 +3723,76 @@ ] } ] + }, + %{ + group: :pleroma, + key: :majic_pool, + type: :group, + description: "Majic/libmagic configuration", + children: [ + %{ + key: :size, + type: :integer, + description: "Number of majic workers to start.", + suggestions: [2] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.User.Backup, + type: :group, + description: "Account Backup", + children: [ + %{ + key: :purge_after_days, + type: :integer, + description: "Remove backup achives after N days", + suggestions: [30] + }, + %{ + key: :limit_days, + type: :integer, + description: "Limit user to export not more often than once per N days", + suggestions: [7] + } + ] + }, + %{ + group: :prometheus, + key: Pleroma.Web.Endpoint.MetricsExporter, + type: :group, + description: "Prometheus app metrics endpoint configuration", + children: [ + %{ + key: :enabled, + type: :boolean, + description: "[Pleroma extension] Enables app metrics endpoint." + }, + %{ + key: :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." + }, + %{ + key: :auth, + type: [:boolean, :tuple], + description: "Enables HTTP Basic Auth for app metrics endpoint.", + suggestion: [false, {:basic, "myusername", "mypassword"}] + }, + %{ + key: :path, + type: :string, + description: "App metrics endpoint URI path.", + suggestions: ["/api/pleroma/app_metrics"] + }, + %{ + key: :format, + type: :atom, + description: "App metrics endpoint output format.", + suggestions: [:text, :protobuf] + } + ] } ] diff --git a/config/test.exs b/config/test.exs index 93a0e2a61..7cc660e3c 100644 --- a/config/test.exs +++ b/config/test.exs @@ -83,8 +83,6 @@ "BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4", private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA" -config :web_push_encryption, :http_client, Pleroma.Web.WebPushHttpClientMock - config :pleroma, Oban, queues: false, crontab: false, @@ -115,7 +113,7 @@ config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: true -config :pleroma, Pleroma.Plugs.RemoteIp, enabled: false +config :pleroma, Pleroma.Web.Plugs.RemoteIp, enabled: false config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: true diff --git a/coveralls.json b/coveralls.json index 75e845ade..8652591ef 100644 --- a/coveralls.json +++ b/coveralls.json @@ -1,6 +1,7 @@ { "skip_files": [ "test/support", - "lib/mix/tasks/pleroma/benchmark.ex" + "lib/mix/tasks/pleroma/benchmark.ex", + "lib/credo/check/consistency/file_location.ex" ] } \ No newline at end of file diff --git a/docs/API/admin_api.md b/docs/API/admin_api.md index 7992db58f..f7b5bcae7 100644 --- a/docs/API/admin_api.md +++ b/docs/API/admin_api.md @@ -20,12 +20,14 @@ Configuration options: - `external`: only external users - `active`: only active users - `need_approval`: only unapproved users + - `unconfirmed`: only unconfirmed users - `deactivated`: only deactivated users - `is_admin`: users with admin role - `is_moderator`: users with moderator role - *optional* `page`: **integer** page number - *optional* `page_size`: **integer** number of users per page (default is `50`) - *optional* `tags`: **[string]** tags list + - *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` @@ -349,9 +351,9 @@ Response: ### Unfollow a Relay -Params: - -* `relay_url` +- Params: + - `relay_url` + - *optional* `force`: forcefully unfollow a relay even when the relay is not available. (default is `false`) Response: diff --git a/docs/API/chats.md b/docs/API/chats.md index aa6119670..f50144c86 100644 --- a/docs/API/chats.md +++ b/docs/API/chats.md @@ -116,6 +116,10 @@ The modified chat message This will return a list of chats that you have been involved in, sorted by their last update (so new chats will be at the top). +Parameters: + +- with_muted: Include chats from muted users (boolean). + Returned data: ```json @@ -173,11 +177,14 @@ Returned data: "created_at": "2020-04-21T15:06:45.000Z", "emojis": [], "id": "12", - "unread": false + "unread": false, + "idempotency_key": "75442486-0874-440c-9db1-a7006c25a31f" } ] ``` +- idempotency_key: The copy of the `idempotency-key` HTTP request header that can be used for optimistic message sending. Included only during the first few minutes after the message creation. + ### Posting a chat message Posting a chat message for given Chat id works like this: diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md index 38865dc68..bb1000b0b 100644 --- a/docs/API/differences_in_mastoapi_responses.md +++ b/docs/API/differences_in_mastoapi_responses.md @@ -9,9 +9,13 @@ Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mas ## Timelines Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users. + Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`. + Adding the parameter `reply_visibility` to the public and home timelines queries will filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you. +Adding the parameter `instance=lain.com` to the public timeline will show only statuses originating from `lain.com` (or any remote instance). + ## Statuses - `visibility`: has an additional possible value `list` @@ -249,6 +253,8 @@ Has these additional fields under the `pleroma` object: There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field. +For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`. + ## Not implemented Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority. diff --git a/docs/API/pleroma_api.md b/docs/API/pleroma_api.md index 94b6a4fda..7a0a80dad 100644 --- a/docs/API/pleroma_api.md +++ b/docs/API/pleroma_api.md @@ -378,44 +378,43 @@ 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/packs/import` -### Imports packs from filesystem +## `GET /api/pleroma/emoji/pack?name=:name` + +### Get pack.json for the pack + * Method `GET` -* Authentication: required -* Params: None -* Response: JSON, returns a list of imported packs. - -## `GET /api/pleroma/emoji/packs/remote` -### Make request to another instance for packs list -* Method `GET` -* Authentication: required +* Authentication: not required * Params: - * `url`: url of the instance to get packs from -* Response: JSON with the pack list, hashmap with pack name and pack contents + * `page`: page number for files (default 1) + * `page_size`: page size for files (default 30) +* Response: JSON, pack json with `files`, `files_count` and `pack` keys with 200 status or 404 if the pack does not exist. -## `POST /api/pleroma/emoji/packs/download` -### Download pack from another instance -* Method `POST` -* Authentication: required -* Params: - * `url`: url of the instance to download from - * `name`: pack to download from that instance - * `as`: (*optional*) name how to save pack -* Response: JSON, "ok" with 200 status if the pack was downloaded, or 500 if there were - errors downloading the pack +```json +{ + "files": {...}, + "files_count": 0, // emoji count in pack + "pack": {...} +} +``` + +## `POST /api/pleroma/emoji/pack?name=:name` -## `POST /api/pleroma/emoji/packs/:name` ### Creates an empty pack + * Method `POST` -* Authentication: required -* Params: None +* Authentication: required (admin) +* Params: + * `name`: pack name * Response: JSON, "ok" and 200 status or 409 if the pack with that name already exists -## `PATCH /api/pleroma/emoji/packs/:name` +## `PATCH /api/pleroma/emoji/pack?name=:name` + ### Updates (replaces) pack metadata + * Method `PATCH` -* Authentication: required +* Authentication: required (admin) * Params: + * `name`: pack name * `metadata`: metadata to replace the old one * `license`: Pack license * `homepage`: Pack home page url @@ -426,39 +425,85 @@ 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/packs/:name` +## `DELETE /api/pleroma/emoji/pack?name=:name` + ### Delete a custom emoji pack + * Method `DELETE` -* Authentication: required -* Params: None +* Authentication: required (admin) +* Params: + * `name`: pack name * Response: JSON, "ok" and 200 status or 500 if there was an error deleting the pack -## `POST /api/pleroma/emoji/packs/:name/files` -### Add new file to the pack -* Method `POST` -* Authentication: required +## `GET /api/pleroma/emoji/packs/import` + +### Imports packs from filesystem + +* Method `GET` +* Authentication: required (admin) +* Params: None +* Response: JSON, returns a list of imported packs. + +## `GET /api/pleroma/emoji/packs/remote` + +### Make request to another instance for packs list + +* Method `GET` +* Authentication: required (admin) * Params: + * `url`: url of the instance to get packs from + * `page`: page number for packs (default 1) + * `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` + +### Download pack from another instance + +* Method `POST` +* Authentication: required (admin) +* Params: + * `url`: url of the instance to download from + * `name`: pack to download from that instance + * `as`: (*optional*) name how to save pack +* 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` + +### Add new file to the pack + +* Method `POST` +* Authentication: required (admin) +* Params: + * `name`: pack name * `file`: file needs to be uploaded with the multipart request or link to remote file. * `shortcode`: (*optional*) shortcode for new emoji, must be unique for all emoji. If not sended, shortcode will be taken from original filename. * `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/:name/files` +## `PATCH /api/pleroma/emoji/packs/files?name=:name` + ### Update emoji file from pack + * Method `PATCH` -* Authentication: required +* Authentication: required (admin) * Params: + * `name`: pack name * `shortcode`: emoji file shortcode * `new_shortcode`: new emoji file shortcode * `new_filename`: new filename for emoji file * `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/:name/files` +## `DELETE /api/pleroma/emoji/packs/files?name=:name` + ### Delete emoji file from pack + * Method `DELETE` -* Authentication: required +* Authentication: required (admin) * Params: + * `name`: pack name * `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. @@ -483,30 +528,14 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa } ``` -## `GET /api/pleroma/emoji/packs/:name` +## `GET /api/pleroma/emoji/packs/archive?name=:name` -### Get pack.json for the pack +### Requests a local pack archive from the instance * Method `GET` * Authentication: not required * Params: - * `page`: page number for files (default 1) - * `page_size`: page size for files (default 30) -* Response: JSON, pack json with `files`, `files_count` and `pack` keys with 200 status or 404 if the pack does not exist. - -```json -{ - "files": {...}, - "files_count": 0, // emoji count in pack - "pack": {...} -} -``` - -## `GET /api/pleroma/emoji/packs/:name/archive` -### Requests a local pack archive from the instance -* Method `GET` -* Authentication: not required -* Params: None + * `name`: pack name * Response: the archive of the pack with a 200 status code, 403 if the pack is not set as shared, 404 if the pack does not exist @@ -586,3 +615,41 @@ Emoji reactions work a lot like favourites do. They make it possible to react to {"name": "😀", "count": 2, "me": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]} ] ``` + +## `POST /api/v1/pleroma/backups` +### Create a user backup archive + +* Method: `POST` +* Authentication: required +* Params: none +* Response: JSON +* Example response: + +```json +[{ + "content_type": "application/zip", + "file_size": 0, + "inserted_at": "2020-09-10T16:18:03.000Z", + "processed": false, + "url": "https://example.com/media/backups/archive-foobar-20200910T161803-QUhx6VYDRQ2wfV0SdA2Pfj_2CLM_ATUlw-D5l5TJf4Q.zip" +}] +``` + +## `GET /api/v1/pleroma/backups` +### Lists user backups + +* Method: `GET` +* Authentication: not required +* Params: none +* Response: JSON +* Example response: + +```json +[{ + "content_type": "application/zip", + "file_size": 55457, + "inserted_at": "2020-09-10T16:18:03.000Z", + "processed": true, + "url": "https://example.com/media/backups/archive-foobar-20200910T161803-QUhx6VYDRQ2wfV0SdA2Pfj_2CLM_ATUlw-D5l5TJf4Q.zip" +}] +``` diff --git a/docs/API/prometheus.md b/docs/API/prometheus.md index 19c564e3c..a5158d905 100644 --- a/docs/API/prometheus.md +++ b/docs/API/prometheus.md @@ -2,15 +2,37 @@ Pleroma includes support for exporting metrics via the [prometheus_ex](https://github.com/deadtrickster/prometheus.ex) library. +Config example: + +``` +config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, + enabled: true, + auth: {:basic, "myusername", "mypassword"}, + ip_whitelist: ["127.0.0.1"], + path: "/api/pleroma/app_metrics", + format: :text +``` + +* `enabled` (Pleroma extension) enables the endpoint +* `ip_whitelist` (Pleroma extension) could be used to restrict access only to specified IPs +* `auth` sets the authentication (`false` for no auth; configurable to HTTP Basic Auth, see [prometheus-plugs](https://github.com/deadtrickster/prometheus-plugs#exporting) documentation) +* `format` sets the output format (`:text` or `:protobuf`) +* `path` sets the path to app metrics page + + ## `/api/pleroma/app_metrics` + ### Exports Prometheus application metrics + * Method: `GET` -* Authentication: not required +* Authentication: not required by default (see configuration options above) * Params: none -* Response: JSON +* Response: text ## Grafana + ### Config example + The following is a config example to use with [Grafana](https://grafana.com) ``` diff --git a/docs/administration/CLI_tasks/email.md b/docs/administration/CLI_tasks/email.md index 00d2e74f8..d9aa0e71b 100644 --- a/docs/administration/CLI_tasks/email.md +++ b/docs/administration/CLI_tasks/email.md @@ -1,4 +1,4 @@ -# Managing emails +# EMail administration tasks {! backend/administration/CLI_tasks/general_cli_task_info.include !} @@ -30,3 +30,17 @@ Example: ```sh mix pleroma.email test --to root@example.org ``` + +## Send confirmation emails to all unconfirmed user accounts + +=== "OTP" + + ```sh + ./bin/pleroma_ctl email send_confirmation_mails + ``` + +=== "From Source" + + ```sh + mix pleroma.email send_confirmation_mails + ``` diff --git a/docs/administration/CLI_tasks/frontend.md b/docs/administration/CLI_tasks/frontend.md index 7d1c1e937..d4a48cb56 100644 --- a/docs/administration/CLI_tasks/frontend.md +++ b/docs/administration/CLI_tasks/frontend.md @@ -1,12 +1,23 @@ # Managing frontends -`mix pleroma.frontend install [--ref ] [--file ] [--build-url ] [--path ] [--build-dir ]` +=== "OTP" + + ```sh + ./bin/pleroma_ctl frontend install [--ref ] [--file ] [--build-url ] [--path ] [--build-dir ] + ``` + +=== "From Source" + + ```sh + mix pleroma.frontend install [--ref ] [--file ] [--build-url ] [--path ] [--build-dir ] + ``` Frontend can be installed either from local zip file, or automatically downloaded from the web. -You can give all the options directly on the command like, but missing information will be filled out by looking at the data configured under `frontends.available` in the config files. +You can give all the options directly on the command line, but missing information will be filled out by looking at the data configured under `frontends.available` in the config files. + +Currently, known `` values are: -Currently known `` values are: - [admin-fe](https://git.pleroma.social/pleroma/admin-fe) - [kenoma](http://git.pleroma.social/lambadalambda/kenoma) - [pleroma-fe](http://git.pleroma.social/pleroma/pleroma-fe) @@ -19,51 +30,67 @@ You can still install frontends that are not configured, see below. For a frontend configured under the `available` key, it's enough to install it by name. -```sh tab="OTP" -./bin/pleroma_ctl frontend install pleroma -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.frontend install pleroma -``` + ```sh + ./bin/pleroma_ctl frontend install pleroma + ``` -This will download the latest build for the the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`). +=== "From Source" -You can override any of the details. To install a pleroma build from a different url, you could do this: + ```sh + mix pleroma.frontend install pleroma + ``` -```sh tab="OPT" -./bin/pleroma_ctl frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip -``` +This will download the latest build for the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`). -```sh tab="From Source" -mix pleroma.frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip -``` +You can override any of the details. To install a pleroma build from a different URL, you could do this: + +=== "OTP" + + ```sh + ./bin/pleroma_ctl frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip + ``` + +=== "From Source" + + ```sh + mix pleroma.frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip + ``` Similarly, you can also install from a local zip file. -```sh tab="OTP" -./bin/pleroma_ctl frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip -``` + ```sh + ./bin/pleroma_ctl frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip + ``` -The resulting frontend will always be installed into a folder of this template: `${instance_static}/frontends/${name}/${ref}` +=== "From Source" -Careful: This folder will be completely replaced on installation + ```sh + mix pleroma.frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip + ``` + +The resulting frontend will always be installed into a folder of this template: `${instance_static}/frontends/${name}/${ref}`. + +Careful: This folder will be completely replaced on installation. ## Example installation for an unknown frontend -The installation process is the same, but you will have to give all the needed options on the commond line. For example: +The installation process is the same, but you will have to give all the needed options on the command line. For example: -```sh tab="OTP" -./bin/pleroma_ctl frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip -``` + ```sh + ./bin/pleroma_ctl frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip + ``` -If you don't have a zip file but just want to install a frontend from a local path, you can simply copy the files over a folder of this template: `${instance_static}/frontends/${name}/${ref}` +=== "From Source" + + ```sh + mix pleroma.frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip + ``` + +If you don't have a zip file but just want to install a frontend from a local path, you can simply copy the files over a folder of this template: `${instance_static}/frontends/${name}/${ref}`. diff --git a/docs/administration/CLI_tasks/instance.md b/docs/administration/CLI_tasks/instance.md index 989ecc55d..982b22bf3 100644 --- a/docs/administration/CLI_tasks/instance.md +++ b/docs/administration/CLI_tasks/instance.md @@ -37,3 +37,8 @@ If any of the options are left unspecified, you will be prompted interactively. - `--static-dir ` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.) - `--listen-ip ` - the ip the app should listen to, defaults to 127.0.0.1 - `--listen-port ` - the port the app should listen to, defaults to 4000 +- `--strip-uploads ` - use ExifTool to strip uploads of sensitive location data +- `--anonymize-uploads ` - randomize uploaded filenames +- `--dedupe-uploads ` - store files based on their hash to reduce data storage requirements if duplicates are uploaded with different filenames +- `--skip-release-env` - skip generation the release environment file +- `--release-env-file` - release environment file path diff --git a/docs/administration/CLI_tasks/release_environments.md b/docs/administration/CLI_tasks/release_environments.md new file mode 100644 index 000000000..36ab43864 --- /dev/null +++ b/docs/administration/CLI_tasks/release_environments.md @@ -0,0 +1,9 @@ +# Generate release environment file + +```sh tab="OTP" + ./bin/pleroma_ctl release_env gen +``` + +```sh tab="From Source" +mix pleroma.release_env gen +``` diff --git a/docs/administration/CLI_tasks/user.md b/docs/administration/CLI_tasks/user.md index 3e7f028ba..c64ed4f22 100644 --- a/docs/administration/CLI_tasks/user.md +++ b/docs/administration/CLI_tasks/user.md @@ -224,9 +224,10 @@ ``` ### Options +- `--admin`/`--no-admin` - whether the user should be an admin +- `--confirmed`/`--no-confirmed` - whether the user account is confirmed - `--locked`/`--no-locked` - whether the user should be locked - `--moderator`/`--no-moderator` - whether the user should be a moderator -- `--admin`/`--no-admin` - whether the user should be an admin ## Add tags to a user @@ -271,3 +272,33 @@ ```sh mix pleroma.user toggle_confirmed ``` + +## Set confirmation status for all regular active users +*Admins and moderators are excluded* + +=== "OTP" + + ```sh + ./bin/pleroma_ctl user confirm_all + ``` + +=== "From Source" + + ```sh + mix pleroma.user confirm_all + ``` + +## Revoke confirmation status for all regular active users +*Admins and moderators are excluded* + +=== "OTP" + + ```sh + ./bin/pleroma_ctl user unconfirm_all + ``` + +=== "From Source" + + ```sh + mix pleroma.user unconfirm_all + ``` diff --git a/docs/administration/backup.md b/docs/administration/backup.md index be57bf74a..5f279ab97 100644 --- a/docs/administration/backup.md +++ b/docs/administration/backup.md @@ -5,20 +5,25 @@ 1. Stop the Pleroma service. 2. Go to the working directory of Pleroma (default is `/opt/pleroma`) 3. Run `sudo -Hu postgres pg_dump -d --format=custom -f ` (make sure the postgres user has write access to the destination file) -4. Copy `pleroma.pgdump`, `config/prod.secret.exs` and the `uploads` folder to your backup destination. If you have other modifications, copy those changes too. +4. Copy `pleroma.pgdump`, `config/prod.secret.exs`, `config/setup_db.psql` (if still available) and the `uploads` folder to your backup destination. If you have other modifications, copy those changes too. 5. Restart the Pleroma service. ## Restore/Move -1. Optionally reinstall Pleroma (either on the same server or on another server if you want to move servers). Try to use the same database name. +1. Optionally reinstall Pleroma (either on the same server or on another server if you want to move servers). 2. Stop the Pleroma service. 3. Go to the working directory of Pleroma (default is `/opt/pleroma`) 4. Copy the above mentioned files back to their original position. -5. Drop the existing database and recreate an empty one `sudo -Hu postgres psql -c 'DROP DATABASE ;';` `sudo -Hu postgres psql -c 'CREATE DATABASE ;';` -6. Run `sudo -Hu postgres pg_restore -d -v -1 ` -7. If you installed a newer Pleroma version, you should run `mix ecto.migrate`[^1]. This task performs database migrations, if there were any. -8. Restart the Pleroma service. -9. Run `sudo -Hu postgres vacuumdb --all --analyze-in-stages`. This will quickly generate the statistics so that postgres can properly plan queries. +5. Drop the existing database and user if restoring in-place. `sudo -Hu postgres psql -c 'DROP DATABASE ;';` `sudo -Hu postgres psql -c 'DROP USER ;'` +6. Restore the database schema and pleroma postgres role the with the original `setup_db.psql` if you have it: `sudo -Hu postgres psql -f config/setup_db.psql`. + + Alternatively, run the `mix pleroma.instance gen` task again. You can ignore most of the questions, but make the database user, name, and password the same as found in your backup of `config/prod.secret.exs`. Then run the restoration of the pleroma role and schema with of the generated `config/setup_db.psql` as instructed above. You may delete the `config/generated_config.exs` file as it is not needed. + +7. Now restore the Pleroma instance's data into the empty database schema: `sudo -Hu postgres pg_restore -d -v -1 ` +8. If you installed a newer Pleroma version, you should run `mix ecto.migrate`[^1]. This task performs database migrations, if there were any. +9. Restart the Pleroma service. +10. Run `sudo -Hu postgres vacuumdb --all --analyze-in-stages`. This will quickly generate the statistics so that postgres can properly plan queries. +11. If setting up on a new server configure Nginx by using the `installation/pleroma.nginx` config sample or reference the Pleroma installation guide for your OS which contains the Nginx configuration instructions. [^1]: Prefix with `MIX_ENV=prod` to run it using the production config file. @@ -31,6 +36,6 @@ 3. Disable pleroma from systemd `systemctl disable pleroma` 4. Remove the files and folders you created during installation (see installation guide). This includes the pleroma, nginx and systemd files and folders. 5. Reload nginx now that the configuration is removed `systemctl reload nginx` -6. Remove the database and database user `sudo -Hu postgres psql -c 'DROP DATABASE ;';` `sudo -Hu postgres psql -c 'DROP USER ;';` +6. Remove the database and database user `sudo -Hu postgres psql -c 'DROP DATABASE ;';` `sudo -Hu postgres psql -c 'DROP USER ;'` 7. Remove the system user `userdel pleroma` 8. Remove the dependencies that you don't need anymore (see installation guide). Make sure you don't remove packages that are still needed for other software that you have running! diff --git a/docs/ap_extensions.md b/docs/ap_extensions.md index c4550a1ac..3d1caeb3e 100644 --- a/docs/ap_extensions.md +++ b/docs/ap_extensions.md @@ -1,11 +1,41 @@ -# ChatMessages +# AP Extensions +## Actor endpoints -ChatMessages are the messages sent in 1-on-1 chats. They are similar to +The following endpoints are additionally present into our actors. + +- `oauthRegistrationEndpoint` (`http://litepub.social/ns#oauthRegistrationEndpoint`) +- `uploadMedia` (`https://www.w3.org/ns/activitystreams#uploadMedia`) + +### oauthRegistrationEndpoint + +Points to MastodonAPI `/api/v1/apps` for now. + +See + +### uploadMedia + +Inspired by , it is part of the ActivityStreams namespace because it used to be part of the ActivityPub specification and got removed from it. + +Content-Type: multipart/form-data + +Parameters: +- (required) `file`: The file being uploaded +- (optionnal) `description`: A plain-text description of the media, for accessibility purposes. + +Response: HTTP 201 Created with the object into the body, no `Location` header provided as it doesn't have an `id` + +The object given in the reponse should then be inserted into an Object's `attachment` field. + +## ChatMessages + +`ChatMessage`s are the messages sent in 1-on-1 chats. They are similar to `Note`s, but the addresing is done by having a single AP actor in the `to` field. Addressing multiple actors is not allowed. These messages are always private, there is no public version of them. They are created with a `Create` activity. +They are part of the `litepub` namespace as `http://litepub.social/ns#ChatMessage`. + Example: ```json diff --git a/docs/clients.md b/docs/clients.md index f84295b1f..3d81763e1 100644 --- a/docs/clients.md +++ b/docs/clients.md @@ -7,97 +7,105 @@ Feel free to contact us to be added to this list! - Homepage: - Source Code: - Platforms: Windows, Mac, Linux -- Features: Streaming Ready +- Features: MastoAPI, Streaming Ready ### Social - Source Code: - Contact: [@brainblasted@social.libre.fi](https://social.libre.fi/users/brainblasted) - Platforms: Linux (GNOME) - Note(2019-01-28): Not at a pre-alpha stage yet +- Features: MastoAPI ### Whalebird - Homepage: - Source Code: - Contact: [@h3poteto@pleroma.io](https://pleroma.io/users/h3poteto) - Platforms: Windows, Mac, Linux -- Features: Streaming Ready +- Features: MastoAPI, Streaming Ready ## Handheld +### AndStatus +- Homepage: +- Source Code: +- Platforms: Android +- Features: MastoAPI, ActivityPub (Client-to-Server) + ### Amaroq - Homepage: - Source Code: - Contact: [@eurasierboy@mastodon.social](https://mastodon.social/users/eurasierboy) - Platforms: iOS -- Features: No Streaming +- Features: MastoAPI, No Streaming ### Fedilab - Homepage: - Source Code: - Contact: [@fedilab@framapiaf.org](https://framapiaf.org/users/fedilab) - Platforms: Android -- Features: Streaming Ready, Moderation, Text Formatting +- Features: MastoAPI, Streaming Ready, Moderation, Text Formatting ### Kyclos - Source Code: - Platforms: SailfishOS -- Features: No Streaming +- Features: MastoAPI, No Streaming ### Husky - Source code: - Contact: [@Husky@enigmatic.observer](https://enigmatic.observer/users/Husky) - Platforms: Android -- Features: No Streaming, Emoji Reactions, Text Formatting, FE Stickers +- Features: MastoAPI, No Streaming, Emoji Reactions, Text Formatting, FE Stickers ### Fedi - Homepage: - Source Code: Proprietary, but gratis - Platforms: iOS, Android -- Features: Pleroma-specific features like Reactions +- Features: MastoAPI, Pleroma-specific features like Reactions ### Tusky - Homepage: - Source Code: - Contact: [@ConnyDuck@mastodon.social](https://mastodon.social/users/ConnyDuck) - Platforms: Android -- Features: No Streaming +- Features: MastoAPI, No Streaming ### Twidere - Homepage: - Source Code: - Contact: - Platform: Android -- Features: No Streaming +- Features: MastoAPI, No Streaming ### Indigenous - Homepage: - Source Code: -- Contact: [@realize.be@realize.be](@realize.be@realize.be) +- Contact: [@swentel@realize.be](https://realize.be) - Platforms: Android -- Features: No Streaming +- Features: MastoAPI, No Streaming ## Alternative Web Interfaces ### Brutaldon - Homepage: - Source Code: - Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc) -- Features: No Streaming +- Features: MastoAPI, No Streaming ### Halcyon - Source Code: - Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon) -- Features: Streaming Ready +- Features: MastoAPI, Streaming Ready ### Pinafore - Homepage: - Source Code: - Contact: [@pinafore@mastodon.technology](https://mastodon.technology/users/pinafore) - Note: Pleroma support is a secondary goal -- Features: No Streaming +- Features: MastoAPI, No Streaming ### Sengi - Homepage: - Source Code: - Contact: [@sengi_app@mastodon.social](https://mastodon.social/users/sengi_app) +- Features: MastoAPI ### DashFE - Source Code: @@ -107,3 +115,4 @@ Feel free to contact us to be added to this list! - Source Code: - Contact: [@r@freesoftwareextremist.com](https://freesoftwareextremist.com/users/r) - Features: Does not requires JavaScript +- Features: MastoAPI diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 42e5fe808..ebf95ebc9 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -45,6 +45,7 @@ To add configuration to your config file, you can copy it from the base config. older software for theses nicknames. * `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature. * `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow. +* `autofollowing_nicknames`: Set to nicknames of (local) users that automatically follows every newly registered user. * `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`. @@ -113,7 +114,7 @@ To add configuration to your config file, you can copy it from the base config. * `Pleroma.Web.ActivityPub.MRF.MentionPolicy`: Drops posts mentioning configurable users. (See [`:mrf_mention`](#mrf_mention)). * `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (See [`:mrf_vocabulary`](#mrf_vocabulary)). * `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.ActivityExpiration` to be enabled for processing the scheduled delections. + * `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. * `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. @@ -219,12 +220,6 @@ config :pleroma, :mrf_user_allowlist, %{ * `total_user_limit`: the number of scheduled activities a user is allowed to create in total (Default: `300`) * `enabled`: whether scheduled activities are sent to the job queue to be executed -## Pleroma.ActivityExpiration - -Enables the worker which processes posts scheduled for deletion. Pinned posts are exempt from expiration. - -* `enabled`: whether expired activities will be sent to the job queue to be deleted - ## FedSockets FedSockets is an experimental feature allowing for Pleroma backends to federate using a persistant websocket connection as opposed to making each federation a seperate http connection. This feature is currently off by default. It is configurable throught he following options. @@ -416,25 +411,25 @@ This will make Pleroma listen on `127.0.0.1` port `8080` and generate urls start * ``referrer_policy``: The referrer policy to use, either `"same-origin"` or `"no-referrer"`. * ``report_uri``: Adds the specified url to `report-uri` and `report-to` group in CSP header. -### Pleroma.Plugs.RemoteIp +### Pleroma.Web.Plugs.RemoteIp !!! warning If your instance is not behind at least one reverse proxy, you should not enable this plug. -`Pleroma.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. +`Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. Available options: * `enabled` - Enable/disable the plug. Defaults to `false`. -* `headers` - A list of strings naming the `req_headers` to use when deriving the `remote_ip`. Order does not matter. Defaults to `["x-forwarded-for"]`. -* `proxies` - A list of strings in [CIDR](https://en.wikipedia.org/wiki/CIDR) notation specifying the IPs of known proxies. Defaults to `[]`. -* `reserved` - Defaults to [localhost](https://en.wikipedia.org/wiki/Localhost) and [private network](https://en.wikipedia.org/wiki/Private_network). +* `headers` - A list of strings naming the HTTP headers to use when deriving the true client IP address. Defaults to `["x-forwarded-for"]`. +* `proxies` - A list of upstream proxy IP subnets in CIDR notation from which we will parse the content of `headers`. Defaults to `[]`. IPv4 entries without a bitmask will be assumed to be /32 and IPv6 /128. +* `reserved` - A list of reserved IP subnets in CIDR notation which should be ignored if found in `headers`. Defaults to `["127.0.0.0/8", "::1/128", "fc00::/7", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]`. ### :rate_limit !!! note - If your instance is behind a reverse proxy ensure [`Pleroma.Plugs.RemoteIp`](#pleroma-plugs-remoteip) is enabled (it is enabled by default). + If your instance is behind a reverse proxy ensure [`Pleroma.Web.Plugs.RemoteIp`](#pleroma-plugs-remoteip) is enabled (it is enabled by default). A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where: @@ -1083,6 +1078,20 @@ Control favicons for instances. * `enabled`: Allow/disallow displaying and getting instances favicons +## Pleroma.User.Backup + +!!! note + Requires enabled email + +* `:purge_after_days` an integer, remove backup achives after N days. +* `:limit_days` an integer, limit user to export not more often than once per N days. +* `:dir` a string with a path to backup temporary directory or `nil` to let Pleroma choose temporary directory in the following order: + 1. the directory named by the TMPDIR environment variable + 2. the directory named by the TEMP environment variable + 3. the directory named by the TMP environment variable + 4. C:\TMP on Windows or /tmp on Unix-like operating systems + 5. as a last resort, the current working directory + ## Frontend management Frontends in Pleroma are swappable - you can specify which one to use here. diff --git a/docs/configuration/howto_ejabberd.md b/docs/configuration/howto_ejabberd.md new file mode 100644 index 000000000..520a0acbc --- /dev/null +++ b/docs/configuration/howto_ejabberd.md @@ -0,0 +1,136 @@ +# Configuring Ejabberd (XMPP Server) to use Pleroma for authentication + +If you want to give your Pleroma users an XMPP (chat) account, you can configure [Ejabberd](https://github.com/processone/ejabberd) to use your Pleroma server for user authentication, automatically giving every local user an XMPP account. + +In general, you just have to follow the configuration described at [https://docs.ejabberd.im/admin/configuration/authentication/#external-script](https://docs.ejabberd.im/admin/configuration/authentication/#external-script). Please read this section carefully. + +Copy the script below to suitable path on your system and set owner and permissions. Also do not forget adjusting `PLEROMA_HOST` and `PLEROMA_PORT`, if necessary. + +```bash +cp pleroma_ejabberd_auth.py /etc/ejabberd/pleroma_ejabberd_auth.py +chown ejabberd /etc/ejabberd/pleroma_ejabberd_auth.py +chmod 700 /etc/ejabberd/pleroma_ejabberd_auth.py +``` + +Set external auth params in ejabberd.yaml file: + +```bash +auth_method: [external] +extauth_program: "python3 /etc/ejabberd/pleroma_ejabberd_auth.py" +extauth_instances: 3 +auth_use_cache: false +``` + +Restart / reload your ejabberd service. + +After restarting your Ejabberd server, your users should now be able to connect with their Pleroma credentials. + + +```python +import sys +import struct +import http.client +from base64 import b64encode +import logging + + +PLEROMA_HOST = "127.0.0.1" +PLEROMA_PORT = "4000" +AUTH_ENDPOINT = "/api/v1/accounts/verify_credentials" +USER_ENDPOINT = "/api/v1/accounts" +LOGFILE = "/var/log/ejabberd/pleroma_auth.log" + +logging.basicConfig(filename=LOGFILE, level=logging.INFO) + + +# Pleroma functions +def create_connection(): + return http.client.HTTPConnection(PLEROMA_HOST, PLEROMA_PORT) + + +def verify_credentials(user: str, password: str) -> bool: + user_pass_b64 = b64encode("{}:{}".format( + user, password).encode('utf-8')).decode("ascii") + params = {} + headers = { + "Authorization": "Basic {}".format(user_pass_b64) + } + + try: + conn = create_connection() + conn.request("GET", AUTH_ENDPOINT, params, headers) + + response = conn.getresponse() + if response.status == 200: + return True + + return False + except Exception as e: + logging.info("Can not connect: %s", str(e)) + return False + + +def does_user_exist(user: str) -> bool: + conn = create_connection() + conn.request("GET", "{}/{}".format(USER_ENDPOINT, user)) + + response = conn.getresponse() + if response.status == 200: + return True + + return False + + +def auth(username: str, server: str, password: str) -> bool: + return verify_credentials(username, password) + + +def isuser(username, server): + return does_user_exist(username) + + +def read(): + (pkt_size,) = struct.unpack('>H', bytes(sys.stdin.read(2), encoding='utf8')) + pkt = sys.stdin.read(pkt_size) + cmd = pkt.split(':')[0] + if cmd == 'auth': + username, server, password = pkt.split(':', 3)[1:] + write(auth(username, server, password)) + elif cmd == 'isuser': + username, server = pkt.split(':', 2)[1:] + write(isuser(username, server)) + elif cmd == 'setpass': + # u, s, p = pkt.split(':', 3)[1:] + write(False) + elif cmd == 'tryregister': + # u, s, p = pkt.split(':', 3)[1:] + write(False) + elif cmd == 'removeuser': + # u, s = pkt.split(':', 2)[1:] + write(False) + elif cmd == 'removeuser3': + # u, s, p = pkt.split(':', 3)[1:] + write(False) + else: + write(False) + + +def write(result): + if result: + sys.stdout.write('\x00\x02\x00\x01') + else: + sys.stdout.write('\x00\x02\x00\x00') + sys.stdout.flush() + + +if __name__ == "__main__": + logging.info("Starting pleroma ejabberd auth daemon...") + while True: + try: + read() + except Exception as e: + logging.info( + "Error while processing data from ejabberd %s", str(e)) + pass + +``` \ No newline at end of file diff --git a/docs/dev.md b/docs/dev.md index 9c749c17c..22e0691f1 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -6,7 +6,7 @@ This document contains notes and guidelines for Pleroma developers. * Pleroma supports hierarchical OAuth scopes, just like Mastodon but with added granularity of admin scopes. For a reference, see [Mastodon OAuth scopes](https://docs.joinmastodon.org/api/oauth-scopes/). -* It is important to either define OAuth scope restrictions or explicitly mark OAuth scope check as skipped, for every controller action. To define scopes, call `plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: [...]})`. To explicitly set OAuth scopes check skipped, call `plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug )`. +* It is important to either define OAuth scope restrictions or explicitly mark OAuth scope check as skipped, for every controller action. To define scopes, call `plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: [...]})`. To explicitly set OAuth scopes check skipped, call `plug(:skip_plug, Pleroma.Web.Plugs.OAuthScopesPlug )`. * In controllers, `use Pleroma.Web, :controller` will result in `action/2` (see `Pleroma.Web.controller/0` for definition) be called prior to actual controller action, and it'll perform security / privacy checks before passing control to actual controller action. @@ -16,7 +16,7 @@ This document contains notes and guidelines for Pleroma developers. ## [HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) -* With HTTP Basic Auth, OAuth scopes check is _not_ performed for any action (since password is provided during the auth, requester is able to obtain a token with full permissions anyways). `Pleroma.Plugs.AuthenticationPlug` and `Pleroma.Plugs.LegacyAuthenticationPlug` both call `Pleroma.Plugs.OAuthScopesPlug.skip_plug(conn)` when password is provided. +* With HTTP Basic Auth, OAuth scopes check is _not_ performed for any action (since password is provided during the auth, requester is able to obtain a token with full permissions anyways). `Pleroma.Web.Plugs.AuthenticationPlug` and `Pleroma.Web.Plugs.LegacyAuthenticationPlug` both call `Pleroma.Web.Plugs.OAuthScopesPlug.skip_plug(conn)` when password is provided. ## Auth-related configuration, OAuth consumer mode etc. diff --git a/docs/installation/alpine_linux_en.md b/docs/installation/alpine_linux_en.md index a5683f18c..62f2fb778 100644 --- a/docs/installation/alpine_linux_en.md +++ b/docs/installation/alpine_linux_en.md @@ -13,6 +13,7 @@ It assumes that you have administrative rights, either as root or a user with [s * `erlang-parsetools` * `erlang-xmerl` * `git` +* `file-dev` * Development Tools * `cmake` @@ -20,6 +21,9 @@ It assumes that you have administrative rights, either as root or a user with [s * `nginx` (preferred, example configs for other reverse proxies can be found in the repo) * `certbot` (or any other ACME client for Let’s Encrypt certificates) +* `ImageMagick` +* `ffmpeg` +* `exiftool` ### Prepare the system @@ -29,7 +33,6 @@ It assumes that you have administrative rights, either as root or a user with [s awk 'NR==2' /etc/apk/repositories | sed 's/main/community/' | tee -a /etc/apk/repositories ``` - * Then update the system, if not already done: ```shell @@ -40,7 +43,7 @@ sudo apk upgrade * Install some tools, which are needed later: ```shell -sudo apk add git build-base cmake +sudo apk add git build-base cmake file-dev ``` ### Install Elixir and Erlang @@ -56,6 +59,7 @@ sudo apk add erlang erlang-runtime-tools erlang-xmerl elixir ```shell sudo apk add erlang-eldap ``` + ### Install PostgreSQL * Install Postgresql server: @@ -76,6 +80,12 @@ 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)) + +```shell +sudo apk add ffmpeg imagemagick exiftool +``` + ### Install PleromaBE * Add a new system user for the Pleroma service: diff --git a/docs/installation/arch_linux_en.md b/docs/installation/arch_linux_en.md index 7fb69dd60..0eb6d2d5f 100644 --- a/docs/installation/arch_linux_en.md +++ b/docs/installation/arch_linux_en.md @@ -10,11 +10,15 @@ This guide will assume that you have administrative rights, either as root or a * `git` * `base-devel` * `cmake` +* `file` #### Optional packages used in this guide * `nginx` (preferred, example configs for other reverse proxies can be found in the repo) * `certbot` (or any other ACME client for Let’s Encrypt certificates) +* `ImageMagick` +* `ffmpeg` +* `exiftool` ### Prepare the system @@ -27,7 +31,7 @@ sudo pacman -Syu * Install some of the above mentioned programs: ```shell -sudo pacman -S git base-devel elixir cmake +sudo pacman -S git base-devel elixir cmake file ``` ### Install PostgreSQL @@ -52,6 +56,12 @@ 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)) + +```shell +sudo pacman -S ffmpeg imagemagick perl-image-exiftool +``` + ### Install PleromaBE * Add a new system user for the Pleroma service: diff --git a/docs/installation/debian_based_en.md b/docs/installation/debian_based_en.md index 60c2f47e5..b9fc4e112 100644 --- a/docs/installation/debian_based_en.md +++ b/docs/installation/debian_based_en.md @@ -10,6 +10,7 @@ This guide will assume you are on Debian Stretch. This guide should also work wi * `elixir` (1.8+, Follow the guide to install from the Erlang Solutions repo or use [asdf](https://github.com/asdf-vm/asdf) as the pleroma user) * `erlang-dev` * `erlang-nox` +* `libmagic-dev` * `git` * `build-essential` * `cmake` @@ -18,6 +19,9 @@ This guide will assume you are on Debian Stretch. This guide should also work wi * `nginx` (preferred, example configs for other reverse proxies can be found in the repo) * `certbot` (or any other ACME client for Let’s Encrypt certificates) +* `ImageMagick` +* `ffmpeg` +* `exiftool` ### Prepare the system @@ -31,7 +35,7 @@ sudo apt full-upgrade * Install some of the above mentioned programs: ```shell -sudo apt install git build-essential postgresql postgresql-contrib cmake +sudo apt install git build-essential postgresql postgresql-contrib cmake libmagic-devel ``` ### Install Elixir and Erlang @@ -50,6 +54,12 @@ 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) + +```shell +sudo apt install imagemagick ffmpeg libimage-exiftool-perl +``` + ### Install PleromaBE * Add a new system user for the Pleroma service: @@ -91,6 +101,7 @@ sudo -Hu pleroma mix deps.get 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: ```shell @@ -171,6 +182,7 @@ sudo cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.se ``` * Edit the service file and make sure that all paths fit your installation +* Check that `EnvironmentFile` contains the correct path to the env file. Or generate the env file: `sudo -Hu pleroma mix pleroma.release_env gen` * Enable and start `pleroma.service`: ```shell diff --git a/docs/installation/debian_based_jp.md b/docs/installation/debian_based_jp.md index c2dd840d3..94e22325c 100644 --- a/docs/installation/debian_based_jp.md +++ b/docs/installation/debian_based_jp.md @@ -17,11 +17,15 @@ - `git` - `build-essential` - `cmake` +- `libmagic-dev` #### このガイドで利用している追加パッケージ - `nginx` (おすすめです。他のリバースプロキシを使う場合は、参考となる設定をこのリポジトリから探してください) - `certbot` (または何らかのLet's Encrypt向けACMEクライアント) +- `ImageMagick` +- `ffmpeg` +- `exiftool` ### システムを準備する @@ -33,10 +37,9 @@ sudo apt full-upgrade * 上記に挙げたパッケージをインストールしておきます。 ``` -sudo apt install git build-essential postgresql postgresql-contrib cmake +sudo apt install git build-essential postgresql postgresql-contrib cmake ffmpeg imagemagick libmagic-dev ``` - ### ElixirとErlangをインストールします * Erlangのリポジトリをダウンロードおよびインストールします。 @@ -51,6 +54,12 @@ 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) + +```shell +sudo apt install imagemagick ffmpeg libimage-exiftool-perl +``` + ### Pleroma BE (バックエンド) をインストールします * Pleroma用に新しいユーザーを作ります。 diff --git a/docs/installation/freebsd_en.md b/docs/installation/freebsd_en.md index ca2575d9b..fdcb06c53 100644 --- a/docs/installation/freebsd_en.md +++ b/docs/installation/freebsd_en.md @@ -26,6 +26,12 @@ 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)) + +```shell +# pkg install imagemagick ffmpeg p5-Image-ExifTool +``` + ## Configuring Pleroma Create a user for Pleroma: diff --git a/docs/installation/gentoo_en.md b/docs/installation/gentoo_en.md index 5a676380c..f2380ab72 100644 --- a/docs/installation/gentoo_en.md +++ b/docs/installation/gentoo_en.md @@ -29,12 +29,16 @@ Gentoo quite pointedly does not come with a cron daemon installed, and as such i * `dev-lang/elixir` * `dev-vcs/git` * `dev-util/cmake` +* `sys-apps/file` #### Optional ebuilds used in this guide * `www-servers/nginx` (preferred, example configs for other reverse proxies can be found in the repo) * `app-crypt/certbot` (or any other ACME client for Let’s Encrypt certificates) * `app-crypt/certbot-nginx` (nginx certbot plugin that allows use of the all-powerful `--nginx` flag on certbot) +* `media-gfx/imagemagick` +* `media-video/ffmpeg` +* `media-libs/exiftool` ### Prepare the system @@ -47,7 +51,7 @@ Gentoo quite pointedly does not come with a cron daemon installed, and as such i * Emerge all required the required and suggested software in one go: ```shell - # emerge --ask dev-db/postgresql dev-lang/elixir dev-vcs/git www-servers/nginx app-crypt/certbot app-crypt/certbot-nginx dev-util/cmake + # emerge --ask dev-db/postgresql dev-lang/elixir dev-vcs/git www-servers/nginx app-crypt/certbot app-crypt/certbot-nginx dev-util/cmake sys-apps/file ``` If you would not like to install the optional packages, remove them from this line. @@ -87,6 +91,12 @@ If you do not plan to make any modifications to your Pleroma instance, cloning d Not only does this make it much easier to deploy changes you make, as you can commit and pull from upstream and all that good stuff from the comfort of your local machine then simply `git pull` on your instance server when you're ready to deploy, it also ensures you are compliant with the Affero General Public Licence that Pleroma is licenced under, which stipulates that all network services provided with modified AGPL code must publish their changes on a publicly available internet service and for free. It also makes it much easier to ask for help from and provide help to your fellow Pleroma admins if your public repo always reflects what you are running because it is part of your deployment procedure. +### Install media / graphics packages (optional, see [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md)) + +```shell +# emerge --ask media-video/ffmpeg media-gfx/imagemagick media-libs/exiftool +``` + ### Install PleromaBE * Add a new system user for the Pleroma service and set up default directories: diff --git a/docs/installation/netbsd_en.md b/docs/installation/netbsd_en.md index 6ad0de2f6..d5fa04fdf 100644 --- a/docs/installation/netbsd_en.md +++ b/docs/installation/netbsd_en.md @@ -10,7 +10,7 @@ Pleroma uses. The `mksh` shell is needed to run the Elixir `mix` script. -`# pkgin install acmesh elixir git-base git-docs mksh nginx postgresql11-server postgresql11-client postgresql11-contrib sudo` +`# pkgin install acmesh elixir git-base git-docs mksh nginx postgresql11-server postgresql11-client postgresql11-contrib sudo ffmpeg4 ImageMagick` You can also build these packages using pkgsrc: ``` @@ -44,6 +44,10 @@ 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)) + +`# pkgin install ImageMagick ffmpeg4 p5-Image-ExifTool` + ## Configuring Pleroma Create a user for Pleroma: diff --git a/docs/installation/openbsd_en.md b/docs/installation/openbsd_en.md index eee452845..8092ac379 100644 --- a/docs/installation/openbsd_en.md +++ b/docs/installation/openbsd_en.md @@ -10,20 +10,34 @@ The following packages need to be installed: * elixir * gmake - * ImageMagick * git * postgresql-server * postgresql-contrib * cmake + * ffmpeg + * ImageMagick To install them, run the following command (with doas or as root): ``` -pkg_add elixir gmake ImageMagick git postgresql-server postgresql-contrib cmake +pkg_add elixir gmake git postgresql-server postgresql-contrib cmake ffmpeg ImageMagick ``` Pleroma requires a reverse proxy, OpenBSD has relayd in base (and is used in this guide) and packages/ports are available for nginx (www/nginx) and apache (www/apache-httpd). Independently of the reverse proxy, [acme-client(1)](https://man.openbsd.org/acme-client) can be used to get a certificate from Let's Encrypt. +#### Optional software + +Per [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md): + * ImageMagick + * ffmpeg + * exiftool + +To install the above: + +``` +pkg_add ImageMagick ffmpeg p5-Image-ExifTool +``` + #### Creating the pleroma user Pleroma will be run by a dedicated user, \_pleroma. Before creating it, insert the following lines in login.conf: ``` diff --git a/docs/installation/openbsd_fi.md b/docs/installation/openbsd_fi.md index b5b5056a9..01cf34ab4 100644 --- a/docs/installation/openbsd_fi.md +++ b/docs/installation/openbsd_fi.md @@ -16,7 +16,18 @@ Matrix-kanava #freenode_#pleroma:matrix.org ovat hyviä paikkoja löytää apua Asenna tarvittava ohjelmisto: -`# pkg_add git elixir gmake postgresql-server-10.3 postgresql-contrib-10.3 cmake` +`# pkg_add git elixir gmake postgresql-server-10.3 postgresql-contrib-10.3 cmake ffmpeg ImageMagick` + +#### Optional software + +[`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md): + * ImageMagick + * ffmpeg + * exiftool + +Asenna tarvittava ohjelmisto: + +`# pkg_add ImageMagick ffmpeg p5-Image-ExifTool` Luo postgresql-tietokanta: diff --git a/docs/installation/optional/media_graphics_packages.md b/docs/installation/optional/media_graphics_packages.md new file mode 100644 index 000000000..cb3d71188 --- /dev/null +++ b/docs/installation/optional/media_graphics_packages.md @@ -0,0 +1,32 @@ +# Optional software packages needed for specific functionality + +For specific Pleroma functionality (which is disabled by default) some or all of the below packages are required: + * `ImageMagic` + * `ffmpeg` + * `exiftool` + +Please refer to documentation in `docs/installation` on how to install them on specific OS. + +Note: the packages are not required with the current default settings of Pleroma. + +## `ImageMagick` + +`ImageMagick` is a set of tools to create, edit, compose, or convert bitmap images. + +It is required for the following Pleroma features: + * `Pleroma.Upload.Filters.Mogrify`, `Pleroma.Upload.Filters.Mogrifun` upload filters (related config: `Plaroma.Upload/filters` in `config/config.exs`) + * Media preview proxy for still images (related config: `media_preview_proxy/enabled` in `config/config.exs`) + +## `ffmpeg` + +`ffmpeg` is software to record, convert and stream audio and video. + +It is required for the following Pleroma features: + * Media preview proxy for videos (related config: `media_preview_proxy/enabled` in `config/config.exs`) + +## `exiftool` + +`exiftool` is media files metadata reader/writer. + +It is required for the following Pleroma features: + * `Pleroma.Upload.Filters.Exiftool` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) diff --git a/docs/installation/otp_en.md b/docs/installation/otp_en.md index b7e3bb2ac..98360bcf7 100644 --- a/docs/installation/otp_en.md +++ b/docs/installation/otp_en.md @@ -27,17 +27,37 @@ Other than things bundled in the OTP release Pleroma depends on: * PostgreSQL (also utilizes extensions in postgresql-contrib) * nginx (could be swapped with another reverse proxy but this guide covers only it) * certbot (for Let's Encrypt certificates, could be swapped with another ACME client, but this guide covers only it) +* libmagic/file === "Alpine" ``` echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories apk update - apk add curl unzip ncurses postgresql postgresql-contrib nginx certbot + apk add curl unzip ncurses postgresql postgresql-contrib nginx certbot file-dev ``` === "Debian/Ubuntu" ``` - apt install curl unzip libncurses5 postgresql postgresql-contrib nginx certbot + apt install curl unzip libncurses5 postgresql postgresql-contrib nginx certbot libmagic-dev + ``` + +### Installing optional packages + +Per [`docs/installation/optional/media_graphics_packages.md`](optional/media_graphics_packages.md): + * ImageMagick + * ffmpeg + * exiftool + +=== "Alpine" + ``` + echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories + apk update + apk add imagemagick ffmpeg exiftool + ``` + +=== "Debian/Ubuntu" + ``` + apt install imagemagick ffmpeg libimage-exiftool-perl ``` ## Setup @@ -82,6 +102,8 @@ It is encouraged to check [Optimizing your PostgreSQL performance](../configurat 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" @@ -127,6 +149,9 @@ chown -R pleroma /etc/pleroma # Run the config generator su pleroma -s $SHELL -lc "./bin/pleroma_ctl instance gen --output /etc/pleroma/config.exs --output-psql /tmp/setup_db.psql" +# Run the environment file generator. +su pleroma -s $SHELL -lc "./bin/pleroma_ctl release_env gen" + # Create the postgres database su postgres -s $SHELL -lc "psql -f /tmp/setup_db.psql" @@ -137,7 +162,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 "./bin/pleroma daemon" +su pleroma -s $SHELL -lc "export $(cat /opt/pleroma/config/pleroma.env); ./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 @@ -289,4 +314,3 @@ This will create an account withe the username of 'joeuser' with the email addre ## Questions Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**. - diff --git a/installation/init.d/pleroma b/installation/init.d/pleroma index 384536f7e..e908cda1b 100755 --- a/installation/init.d/pleroma +++ b/installation/init.d/pleroma @@ -8,6 +8,7 @@ pidfile="/var/run/pleroma.pid" directory=/opt/pleroma healthcheck_delay=60 healthcheck_timer=30 +export $(cat /opt/pleroma/config/pleroma.env) : ${pleroma_port:-4000} diff --git a/installation/pleroma.service b/installation/pleroma.service index 5dcbc1387..63e83ed6e 100644 --- a/installation/pleroma.service +++ b/installation/pleroma.service @@ -17,6 +17,8 @@ Environment="MIX_ENV=prod" Environment="HOME=/var/lib/pleroma" ; Path to the folder containing the Pleroma installation. WorkingDirectory=/opt/pleroma +; Path to the environment file. the file contains RELEASE_COOKIE and etc +EnvironmentFile=/opt/pleroma/config/pleroma.env ; Path to the Mix binary. ExecStart=/usr/bin/mix phx.server @@ -29,8 +31,6 @@ ProtectHome=true ProtectSystem=full ; Sets up a new /dev mount for the process and only adds API pseudo devices like /dev/null, /dev/zero or /dev/random but not physical devices. Disabled by default because it may not work on devices like the Raspberry Pi. PrivateDevices=false -; Ensures that the service process and all its children can never gain new privileges through execve(). -NoNewPrivileges=true ; Drops the sysadmin capability from the daemon. CapabilityBoundingSet=~CAP_SYS_ADMIN diff --git a/installation/pleroma.vcl b/installation/pleroma.vcl index 154747aa6..13dad784c 100644 --- a/installation/pleroma.vcl +++ b/installation/pleroma.vcl @@ -1,3 +1,4 @@ +# Recommended varnishncsa logging format: '%h %l %u %t "%m %{X-Forwarded-Proto}i://%{Host}i%U%q %H" %s %b "%{Referer}i" "%{User-agent}i"' vcl 4.1; import std; @@ -14,8 +15,11 @@ acl purge { sub vcl_recv { # Redirect HTTP to HTTPS if (std.port(server.ip) != 443) { + set req.http.X-Forwarded-Proto = "http"; set req.http.x-redir = "https://" + req.http.host + req.url; return (synth(750, "")); + } else { + set req.http.X-Forwarded-Proto = "https"; } # CHUNKED SUPPORT @@ -105,7 +109,7 @@ sub vcl_hash { sub vcl_backend_fetch { # Be more lenient for slow servers on the fediverse - if bereq.url ~ "^/proxy/" { + if (bereq.url ~ "^/proxy/") { set bereq.first_byte_timeout = 300s; } diff --git a/lib/mix/tasks/pleroma/count_statuses.ex b/lib/mix/tasks/pleroma/count_statuses.ex index e1e8195dd..8761d8f17 100644 --- a/lib/mix/tasks/pleroma/count_statuses.ex +++ b/lib/mix/tasks/pleroma/count_statuses.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Mix.Tasks.Pleroma.CountStatuses do @shortdoc "Re-counts statuses for all users" diff --git a/lib/mix/tasks/pleroma/digest.ex b/lib/mix/tasks/pleroma/digest.ex index 3595f912d..cac148b88 100644 --- a/lib/mix/tasks/pleroma/digest.ex +++ b/lib/mix/tasks/pleroma/digest.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Mix.Tasks.Pleroma.Digest do use Mix.Task import Mix.Pleroma diff --git a/lib/mix/tasks/pleroma/docs.ex b/lib/mix/tasks/pleroma/docs.ex index 6088fc71d..ad5c37fc9 100644 --- a/lib/mix/tasks/pleroma/docs.ex +++ b/lib/mix/tasks/pleroma/docs.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Mix.Tasks.Pleroma.Docs do use Mix.Task import Mix.Pleroma diff --git a/lib/mix/tasks/pleroma/ecto/ecto.ex b/lib/mix/tasks/pleroma/ecto.ex similarity index 100% rename from lib/mix/tasks/pleroma/ecto/ecto.ex rename to lib/mix/tasks/pleroma/ecto.ex diff --git a/lib/mix/tasks/pleroma/email.ex b/lib/mix/tasks/pleroma/email.ex index d3fac6ec8..bc5facc09 100644 --- a/lib/mix/tasks/pleroma/email.ex +++ b/lib/mix/tasks/pleroma/email.ex @@ -1,12 +1,16 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Mix.Tasks.Pleroma.Email do use Mix.Task import Mix.Pleroma - @shortdoc "Simple Email test" + @shortdoc "Email administrative tasks" @moduledoc File.read!("docs/administration/CLI_tasks/email.md") def run(["test" | args]) do - Mix.Pleroma.start_pleroma() + start_pleroma() {options, [], []} = OptionParser.parse( @@ -21,4 +25,20 @@ def run(["test" | args]) do shell_info("Test email has been sent to #{inspect(email.to)} from #{inspect(email.from)}") end + + def run(["resend_confirmation_emails"]) do + start_pleroma() + + shell_info("Sending emails to all unconfirmed users") + + Pleroma.User.Query.build(%{ + local: true, + deactivated: false, + confirmation_pending: true, + invisible: false + }) + |> Pleroma.Repo.chunk_stream(500) + |> Stream.each(&Pleroma.User.try_send_confirmation_email(&1)) + |> Stream.run() + end end diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index 91440b453..1915aacd9 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -33,7 +33,12 @@ def run(["gen" | rest]) do uploads_dir: :string, static_dir: :string, listen_ip: :string, - listen_port: :string + listen_port: :string, + strip_uploads: :string, + anonymize_uploads: :string, + dedupe_uploads: :string, + skip_release_env: :boolean, + release_env_file: :string ], aliases: [ o: :output, @@ -158,6 +163,30 @@ def run(["gen" | rest]) do ) |> Path.expand() + strip_uploads = + get_option( + options, + :strip_uploads, + "Do you want to strip location (GPS) data from uploaded images? (y/n)", + "y" + ) === "y" + + anonymize_uploads = + get_option( + options, + :anonymize_uploads, + "Do you want to anonymize the filenames of uploads? (y/n)", + "n" + ) === "y" + + dedupe_uploads = + get_option( + options, + :dedupe_uploads, + "Do you want to deduplicate uploaded files? (y/n)", + "n" + ) === "y" + Config.put([:instance, :static_dir], static_dir) secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) @@ -188,7 +217,13 @@ def run(["gen" | rest]) do uploads_dir: uploads_dir, rum_enabled: rum_enabled, listen_ip: listen_ip, - listen_port: listen_port + listen_port: listen_port, + upload_filters: + upload_filters(%{ + strip: strip_uploads, + anonymize: anonymize_uploads, + dedupe: dedupe_uploads + }) ) result_psql = @@ -208,6 +243,24 @@ def run(["gen" | rest]) do write_robots_txt(static_dir, indexable, template_dir) + if Keyword.get(options, :skip_release_env, false) do + shell_info(""" + Release environment file is skip. Please generate the release env file before start. + `MIX_ENV=#{Mix.env()} mix pleroma.release_env gen` + """) + else + shell_info("Generation the environment file:") + + release_env_args = + with path when not is_nil(path) <- Keyword.get(options, :release_env_file) do + ["gen", "--path", path] + else + _ -> ["gen"] + end + + Mix.Tasks.Pleroma.ReleaseEnv.run(release_env_args) + end + shell_info( "\n All files successfully written! Refer to the installation instructions for your platform for next steps." ) @@ -247,4 +300,31 @@ defp write_robots_txt(static_dir, indexable, template_dir) do File.write(robots_txt_path, robots_txt) shell_info("Writing #{robots_txt_path}.") end + + defp upload_filters(filters) when is_map(filters) do + enabled_filters = + if filters.strip do + [Pleroma.Upload.Filter.ExifTool] + else + [] + end + + enabled_filters = + if filters.anonymize do + enabled_filters ++ [Pleroma.Upload.Filter.AnonymizeFilename] + else + enabled_filters + end + + enabled_filters = + if filters.dedupe do + enabled_filters ++ [Pleroma.Upload.Filter.Dedupe] + else + enabled_filters + end + + enabled_filters + end + + defp upload_filters(_), do: [] end diff --git a/lib/mix/tasks/pleroma/notification_settings.ex b/lib/mix/tasks/pleroma/notification_settings.ex index 00f5ba7bf..f99275de1 100644 --- a/lib/mix/tasks/pleroma/notification_settings.ex +++ b/lib/mix/tasks/pleroma/notification_settings.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Mix.Tasks.Pleroma.NotificationSettings do @shortdoc "Enable&Disable privacy option for push notifications" @moduledoc """ diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex index a6d8d6c1c..bb808ca47 100644 --- a/lib/mix/tasks/pleroma/relay.ex +++ b/lib/mix/tasks/pleroma/relay.ex @@ -21,10 +21,19 @@ def run(["follow", target]) do end end - def run(["unfollow", target]) do + def run(["unfollow", target | rest]) do start_pleroma() - with {:ok, _activity} <- Relay.unfollow(target) do + {options, [], []} = + OptionParser.parse( + rest, + strict: [force: :boolean], + aliases: [f: :force] + ) + + force = Keyword.get(options, :force, false) + + with {:ok, _activity} <- Relay.unfollow(target, %{force: force}) do # put this task to sleep to allow the genserver to push out the messages :timer.sleep(500) else diff --git a/lib/mix/tasks/pleroma/release_env.ex b/lib/mix/tasks/pleroma/release_env.ex new file mode 100644 index 000000000..9da74ffcf --- /dev/null +++ b/lib/mix/tasks/pleroma/release_env.ex @@ -0,0 +1,76 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.ReleaseEnv do + use Mix.Task + import Mix.Pleroma + + @shortdoc "Generate Pleroma environment file." + @moduledoc File.read!("docs/administration/CLI_tasks/release_environments.md") + + def run(["gen" | rest]) do + {options, [], []} = + OptionParser.parse( + rest, + strict: [ + force: :boolean, + path: :string + ], + aliases: [ + p: :path, + f: :force + ] + ) + + file_path = + get_option( + options, + :path, + "Environment file path", + "./config/pleroma.env" + ) + + env_path = Path.expand(file_path) + + proceed? = + if File.exists?(env_path) do + get_option( + options, + :force, + "Environment file already exists. Do you want to overwrite the #{env_path} file? (y/n)", + "n" + ) === "y" + else + true + end + + if proceed? do + case do_generate(env_path) do + {:error, reason} -> + shell_error( + File.Error.message(%{action: "write to file", reason: reason, path: env_path}) + ) + + _ -> + shell_info("\nThe file generated: #{env_path}.\n") + + shell_info(""" + WARNING: before start pleroma app please make sure to make the file read-only and non-modifiable. + Example: + chmod 0444 #{file_path} + chattr +i #{file_path} + """) + end + else + shell_info("\nThe file is exist. #{env_path}.\n") + end + end + + def do_generate(path) do + content = "RELEASE_COOKIE=#{Base.encode32(:crypto.strong_rand_bytes(32))}" + + File.mkdir_p!(Path.dirname(path)) + File.write(path, content) + end +end diff --git a/lib/mix/tasks/pleroma/robotstxt.ex b/lib/mix/tasks/pleroma/robots_txt.ex similarity index 100% rename from lib/mix/tasks/pleroma/robotstxt.ex rename to lib/mix/tasks/pleroma/robots_txt.ex diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index b20c49d89..a8d251411 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -196,17 +196,24 @@ def run(["set", nickname | rest]) do OptionParser.parse( rest, strict: [ - moderator: :boolean, admin: :boolean, - locked: :boolean + confirmed: :boolean, + locked: :boolean, + moderator: :boolean ] ) with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do user = - case Keyword.get(options, :moderator) do + case Keyword.get(options, :admin) do nil -> user - value -> set_moderator(user, value) + value -> set_admin(user, value) + end + + user = + case Keyword.get(options, :confirmed) do + nil -> user + value -> set_confirmed(user, value) end user = @@ -216,9 +223,9 @@ def run(["set", nickname | rest]) do end _user = - case Keyword.get(options, :admin) do + case Keyword.get(options, :moderator) do nil -> user - value -> set_admin(user, value) + value -> set_moderator(user, value) end else _ -> @@ -353,6 +360,42 @@ def run(["toggle_confirmed", nickname]) do end end + def run(["confirm_all"]) do + start_pleroma() + + Pleroma.User.Query.build(%{ + local: true, + deactivated: false, + is_moderator: false, + is_admin: false, + invisible: false + }) + |> Pleroma.Repo.chunk_stream(500, :batches) + |> Stream.each(fn users -> + users + |> Enum.each(fn user -> User.need_confirmation(user, false) end) + end) + |> Stream.run() + end + + def run(["unconfirm_all"]) do + start_pleroma() + + Pleroma.User.Query.build(%{ + local: true, + deactivated: false, + is_moderator: false, + is_admin: false, + invisible: false + }) + |> Pleroma.Repo.chunk_stream(500, :batches) + |> Stream.each(fn users -> + users + |> Enum.each(fn user -> User.need_confirmation(user, true) end) + end) + |> Stream.run() + end + def run(["sign_out", nickname]) do start_pleroma() @@ -376,7 +419,7 @@ def run(["list"]) do |> Enum.each(fn user -> shell_info( "#{user.nickname} moderator: #{user.is_moderator}, admin: #{user.is_admin}, locked: #{ - user.locked + user.is_locked }, deactivated: #{user.deactivated}" ) end) @@ -404,10 +447,17 @@ defp set_admin(user, value) do defp set_locked(user, value) do {:ok, user} = user - |> Changeset.change(%{locked: value}) + |> Changeset.change(%{is_locked: value}) |> User.update_and_set_cache() - shell_info("Locked status of #{user.nickname}: #{user.locked}") + shell_info("Locked status of #{user.nickname}: #{user.is_locked}") + user + end + + defp set_confirmed(user, value) do + {:ok, user} = User.need_confirmation(user, !value) + + shell_info("Confirmation pending status of #{user.nickname}: #{user.confirmation_pending}") user end end diff --git a/lib/transports.ex b/lib/phoenix/transports/web_socket/raw.ex similarity index 90% rename from lib/transports.ex rename to lib/phoenix/transports/web_socket/raw.ex index aab7fad99..c3665bebe 100644 --- a/lib/transports.ex +++ b/lib/phoenix/transports/web_socket/raw.ex @@ -31,7 +31,12 @@ def init(%Plug.Conn{method: "GET"} = conn, {endpoint, handler, transport}) do case conn do %{halted: false} = conn -> - case Transport.connect(endpoint, handler, transport, __MODULE__, nil, conn.params) do + case handler.connect(%{ + endpoint: endpoint, + transport: transport, + options: [serializer: nil], + params: conn.params + }) do {:ok, socket} -> {:ok, conn, {__MODULE__, {socket, opts}}} diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 17af04257..553834da0 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -14,6 +14,7 @@ defmodule Pleroma.Activity do alias Pleroma.ReportNote alias Pleroma.ThreadMute alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub import Ecto.Changeset import Ecto.Query @@ -153,6 +154,18 @@ def get_bookmark(%Activity{} = activity, %User{} = user) do def get_bookmark(_, _), do: nil + def get_report(activity_id) do + opts = %{ + type: "Flag", + skip_preload: true, + preload_report_notes: true + } + + ActivityPub.fetch_activities_query([], opts) + |> where(id: ^activity_id) + |> Repo.one() + end + def change(struct, params \\ %{}) do struct |> cast(params, [:data, :recipients]) diff --git a/lib/pleroma/activity/ir/topics.ex b/lib/pleroma/activity/ir/topics.ex index 9e65bedad..fe2e8cb5c 100644 --- a/lib/pleroma/activity/ir/topics.ex +++ b/lib/pleroma/activity/ir/topics.ex @@ -40,7 +40,8 @@ defp visibility_tags(object, activity) do end defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do - tags ++ hashtags_to_topics(object) ++ attachment_topics(object, activity) + tags ++ + remote_topics(activity) ++ hashtags_to_topics(object) ++ attachment_topics(object, activity) end defp item_creation_tags(tags, _, _) do @@ -55,9 +56,19 @@ defp hashtags_to_topics(%{data: %{"tag" => tags}}) do defp hashtags_to_topics(_), do: [] + defp remote_topics(%{local: true}), do: [] + + defp remote_topics(%{actor: actor}) when is_binary(actor), + do: ["public:remote:" <> URI.parse(actor).host] + + defp remote_topics(_), do: [] + defp attachment_topics(%{data: %{"attachment" => []}}, _act), do: [] defp attachment_topics(_object, %{local: true}), do: ["public:media", "public:local:media"] + defp attachment_topics(_object, %{actor: actor}) when is_binary(actor), + do: ["public:media", "public:remote:media:" <> URI.parse(actor).host] + defp attachment_topics(_object, _act), do: ["public:media"] end diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 00ec79a2a..7c4cd9626 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -52,11 +52,10 @@ def start(_type, _args) do Pleroma.HTML.compile_scrubbers() Pleroma.Config.Oban.warn() Config.DeprecationWarnings.warn() - Pleroma.Plugs.HTTPSecurityPlug.warn_if_disabled() + Pleroma.Web.Plugs.HTTPSecurityPlug.warn_if_disabled() Pleroma.ApplicationRequirements.verify!() setup_instrumenters() load_custom_modules() - check_system_commands() Pleroma.Docs.JSON.compile() adapter = Application.get_env(:tesla, :adapter) @@ -89,18 +88,19 @@ def start(_type, _args) do Pleroma.Repo, Config.TransferTask, Pleroma.Emoji, - Pleroma.Plugs.RateLimiter.Supervisor + Pleroma.Web.Plugs.RateLimiter.Supervisor ] ++ cachex_children() ++ http_children(adapter, @env) ++ [ Pleroma.Stats, Pleroma.JobQueueMonitor, + {Majic.Pool, [name: Pleroma.MajicPool, pool_size: Config.get([:majic_pool, :size], 2)]}, {Oban, Config.get(Oban)} ] ++ task_children(@env) ++ dont_run_in_test(@env) ++ - chat_child(@env, chat_enabled?()) ++ + chat_child(chat_enabled?()) ++ [ Pleroma.Web.Endpoint, Pleroma.Gopher.Server @@ -151,7 +151,10 @@ defp setup_instrumenters do Pleroma.Web.Endpoint.MetricsExporter.setup() Pleroma.Web.Endpoint.PipelineInstrumenter.setup() - Pleroma.Web.Endpoint.Instrumenter.setup() + + # Note: disabled until prometheus-phx is integrated into prometheus-phoenix: + # Pleroma.Web.Endpoint.Instrumenter.setup() + PrometheusPhx.setup() end defp cachex_children do @@ -165,7 +168,11 @@ defp cachex_children do build_cachex("web_resp", limit: 2500), build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10), build_cachex("failed_proxy_url", limit: 2500), - build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000) + build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000), + build_cachex("chat_message_id_idempotency_key", + expiration: chat_message_id_idempotency_key_expiration(), + limit: 500_000 + ) ] end @@ -175,6 +182,9 @@ defp emoji_packs_expiration, defp idempotency_expiration, do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60)) + defp chat_message_id_idempotency_key_expiration, + do: expiration(default: :timer.minutes(2), interval: :timer.seconds(60)) + defp seconds_valid_interval, do: :timer.seconds(Config.get!([Pleroma.Captcha, :seconds_valid])) @@ -202,11 +212,14 @@ defp dont_run_in_test(_) do ] end - defp chat_child(_env, true) do - [Pleroma.Web.ChatChannel.ChatChannelState] + defp chat_child(true) do + [ + Pleroma.Web.ChatChannel.ChatChannelState, + {Phoenix.PubSub, [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]} + ] end - defp chat_child(_, _), do: [] + defp chat_child(_), do: [] defp task_children(:test) do [ @@ -260,21 +273,4 @@ defp http_children(Tesla.Adapter.Gun, _) do end defp http_children(_, _), do: [] - - defp check_system_commands do - filters = Config.get([Pleroma.Upload, :filters]) - - check_filter = fn filter, command_required -> - with true <- filter in filters, - false <- Pleroma.Utils.command_available?(command_required) do - Logger.error( - "#{filter} is specified in list of Pleroma.Upload filters, but the #{command_required} command is not found" - ) - end - end - - check_filter.(Pleroma.Upload.Filters.Exiftool, "exiftool") - check_filter.(Pleroma.Upload.Filters.Mogrify, "mogrify") - check_filter.(Pleroma.Upload.Filters.Mogrifun, "mogrify") - end end diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex index 16f62b6f5..b977257a3 100644 --- a/lib/pleroma/application_requirements.ex +++ b/lib/pleroma/application_requirements.ex @@ -9,6 +9,9 @@ defmodule Pleroma.ApplicationRequirements do defmodule VerifyError, do: defexception([:message]) + alias Pleroma.Config + alias Pleroma.Helpers.MediaHelper + import Ecto.Query require Logger @@ -16,7 +19,8 @@ defmodule VerifyError, do: defexception([:message]) @spec verify!() :: :ok | VerifyError.t() def verify! do :ok - |> check_confirmation_accounts! + |> check_system_commands!() + |> check_confirmation_accounts!() |> check_migrations_applied!() |> check_welcome_message_config!() |> check_rum!() @@ -48,7 +52,9 @@ def check_confirmation_accounts!(:ok) do if Pleroma.Config.get([:instance, :account_activation_required]) && not Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) do Logger.error( - "Account activation enabled, but no Mailer settings enabled.\nPlease set config :pleroma, :instance, account_activation_required: false\nOtherwise setup and enable Mailer." + "Account activation enabled, but no Mailer settings enabled.\n" <> + "Please set config :pleroma, :instance, account_activation_required: false\n" <> + "Otherwise setup and enable Mailer." ) {:error, @@ -81,7 +87,9 @@ def check_migrations_applied!(:ok) do Enum.map(down_migrations, fn {:down, id, name} -> "- #{name} (#{id})\n" end) Logger.error( - "The following migrations were not applied:\n#{down_migrations_text}If you want to start Pleroma anyway, set\nconfig :pleroma, :i_am_aware_this_may_cause_data_loss, disable_migration_check: true" + "The following migrations were not applied:\n#{down_migrations_text}" <> + "If you want to start Pleroma anyway, set\n" <> + "config :pleroma, :i_am_aware_this_may_cause_data_loss, disable_migration_check: true" ) {:error, "Unapplied Migrations detected"} @@ -124,14 +132,22 @@ defp do_check_rum!(setting, migrate) do case {setting, migrate} do {true, false} -> Logger.error( - "Use `RUM` index is enabled, but were not applied migrations for it.\nIf you want to start Pleroma anyway, set\nconfig :pleroma, :database, rum_enabled: false\nOtherwise apply the following migrations:\n`mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/`" + "Use `RUM` index is enabled, but were not applied migrations for it.\n" <> + "If you want to start Pleroma anyway, set\n" <> + "config :pleroma, :database, rum_enabled: false\n" <> + "Otherwise apply the following migrations:\n" <> + "`mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/`" ) {:error, "Unapplied RUM Migrations detected"} {false, true} -> Logger.error( - "Detected applied migrations to use `RUM` index, but `RUM` isn't enable in settings.\nIf you want to use `RUM`, set\nconfig :pleroma, :database, rum_enabled: true\nOtherwise roll `RUM` migrations back.\n`mix ecto.rollback --migrations-path priv/repo/optional_migrations/rum_indexing/`" + "Detected applied migrations to use `RUM` index, but `RUM` isn't enable in settings.\n" <> + "If you want to use `RUM`, set\n" <> + "config :pleroma, :database, rum_enabled: true\n" <> + "Otherwise roll `RUM` migrations back.\n" <> + "`mix ecto.rollback --migrations-path priv/repo/optional_migrations/rum_indexing/`" ) {:error, "RUM Migrations detected"} @@ -140,4 +156,50 @@ defp do_check_rum!(setting, migrate) do :ok end end + + defp check_system_commands!(:ok) do + filter_commands_statuses = [ + check_filter(Pleroma.Upload.Filters.Exiftool, "exiftool"), + check_filter(Pleroma.Upload.Filters.Mogrify, "mogrify"), + check_filter(Pleroma.Upload.Filters.Mogrifun, "mogrify") + ] + + preview_proxy_commands_status = + if !Config.get([:media_preview_proxy, :enabled]) or + MediaHelper.missing_dependencies() == [] do + true + else + Logger.error( + "The following dependencies required by Media preview proxy " <> + "(which is currently enabled) are not installed: " <> + inspect(MediaHelper.missing_dependencies()) + ) + + false + end + + if Enum.all?([preview_proxy_commands_status | filter_commands_statuses], & &1) do + :ok + else + {:error, + "System commands missing. Check logs and see `docs/installation` for more details."} + end + end + + defp check_system_commands!(result), do: result + + defp check_filter(filter, command_required) do + filters = Config.get([Pleroma.Upload, :filters]) + + if filter in filters and not Pleroma.Utils.command_available?(command_required) do + Logger.error( + "#{filter} is specified in list of Pleroma.Upload filters, but the " <> + "#{command_required} command is not found" + ) + + false + else + true + end + end end diff --git a/lib/pleroma/bbs/authenticator.ex b/lib/pleroma/bbs/authenticator.ex index 815de7002..83ebb756d 100644 --- a/lib/pleroma/bbs/authenticator.ex +++ b/lib/pleroma/bbs/authenticator.ex @@ -4,8 +4,8 @@ defmodule Pleroma.BBS.Authenticator do use Sshd.PasswordAuthenticator - alias Pleroma.Plugs.AuthenticationPlug alias Pleroma.User + alias Pleroma.Web.Plugs.AuthenticationPlug def authenticate(username, password) do username = to_string(username) diff --git a/lib/pleroma/captcha/captcha.ex b/lib/pleroma/captcha.ex similarity index 100% rename from lib/pleroma/captcha/captcha.ex rename to lib/pleroma/captcha.ex diff --git a/lib/pleroma/captcha/kocaptcha.ex b/lib/pleroma/captcha/kocaptcha.ex index 337506647..201b55ab4 100644 --- a/lib/pleroma/captcha/kocaptcha.ex +++ b/lib/pleroma/captcha/kocaptcha.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Captcha.Kocaptcha do def new do endpoint = Pleroma.Config.get!([__MODULE__, :endpoint]) - case Tesla.get(endpoint <> "/new") do + case Pleroma.HTTP.get(endpoint <> "/new") do {:error, _} -> %{error: :kocaptcha_service_unavailable} diff --git a/lib/pleroma/captcha/captcha_service.ex b/lib/pleroma/captcha/service.ex similarity index 100% rename from lib/pleroma/captcha/captcha_service.ex rename to lib/pleroma/captcha/service.ex diff --git a/lib/pleroma/chat.ex b/lib/pleroma/chat.ex index 84f8806a0..28007cd9f 100644 --- a/lib/pleroma/chat.ex +++ b/lib/pleroma/chat.ex @@ -18,6 +18,7 @@ defmodule Pleroma.Chat do It is a helper only, to make it easy to display a list of chats with other people, ordered by last bump. The actual messages are retrieved by querying the recipients of the ChatMessages. """ + @type t :: %__MODULE__{} @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true} schema "chats" do @@ -41,16 +42,28 @@ def changeset(struct, params) do |> unique_constraint(:user_id, name: :chats_user_id_recipient_index) end + @spec get_by_user_and_id(User.t(), FlakeId.Ecto.CompatType.t()) :: + {:ok, t()} | {:error, :not_found} + def get_by_user_and_id(%User{id: user_id}, id) do + from(c in __MODULE__, + where: c.id == ^id, + where: c.user_id == ^user_id + ) + |> Repo.find_resource() + end + + @spec get_by_id(FlakeId.Ecto.CompatType.t()) :: t() | nil def get_by_id(id) do - __MODULE__ - |> Repo.get(id) + Repo.get(__MODULE__, id) end + @spec get(FlakeId.Ecto.CompatType.t(), String.t()) :: t() | nil def get(user_id, recipient) do - __MODULE__ - |> Repo.get_by(user_id: user_id, recipient: recipient) + Repo.get_by(__MODULE__, user_id: user_id, recipient: recipient) end + @spec get_or_create(FlakeId.Ecto.CompatType.t(), String.t()) :: + {:ok, t()} | {:error, Ecto.Changeset.t()} def get_or_create(user_id, recipient) do %__MODULE__{} |> changeset(%{user_id: user_id, recipient: recipient}) @@ -62,6 +75,8 @@ def get_or_create(user_id, recipient) do ) end + @spec bump_or_create(FlakeId.Ecto.CompatType.t(), String.t()) :: + {:ok, t()} | {:error, Ecto.Changeset.t()} def bump_or_create(user_id, recipient) do %__MODULE__{} |> changeset(%{user_id: user_id, recipient: recipient}) diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index 98c4dc9c8..59c6b0f58 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -33,39 +33,14 @@ def check_hellthread_threshold do end end - def mrf_user_allowlist do - config = Config.get(:mrf_user_allowlist) - - if config && Enum.any?(config, fn {k, _} -> is_atom(k) end) do - rewritten = - Enum.reduce(Config.get(:mrf_user_allowlist), Map.new(), fn {k, v}, acc -> - Map.put(acc, to_string(k), v) - end) - - Config.put(:mrf_user_allowlist, rewritten) - - Logger.error(""" - !!!DEPRECATION WARNING!!! - As of Pleroma 2.0.7, the `mrf_user_allowlist` setting changed of format. - Pleroma 2.1 will remove support for the old format. Please change your configuration to match this: - - config :pleroma, :mrf_user_allowlist, #{inspect(rewritten, pretty: true)} - """) - - :error - else - :ok - end - end - def warn do with :ok <- check_hellthread_threshold(), - :ok <- mrf_user_allowlist(), :ok <- check_old_mrf_config(), :ok <- check_media_proxy_whitelist_config(), :ok <- check_welcome_message_config(), :ok <- check_gun_pool_options(), - :ok <- check_activity_expiration_config() do + :ok <- check_activity_expiration_config(), + :ok <- check_remote_ip_plug_name() do :ok else _ -> @@ -83,9 +58,9 @@ def check_welcome_message_config do if use_old_config do Logger.error(""" !!!DEPRECATION WARNING!!! - Your config is using the old namespace for Welcome messages configuration. You need to change to the new namespace: - \n* `config :pleroma, :instance, welcome_user_nickname` is now `config :pleroma, :welcome, :direct_message, :sender_nickname` - \n* `config :pleroma, :instance, welcome_message` is now `config :pleroma, :welcome, :direct_message, :message` + Your config is using the old namespace for Welcome messages configuration. You need to convert to the new namespace. e.g., + \n* `config :pleroma, :instance, welcome_user_nickname` and `config :pleroma, :instance, welcome_message` are now equal to: + \n* `config :pleroma, :welcome, direct_message: [enabled: true, sender_nickname: "NICKNAME", message: "Your welcome message"]`" """) :error @@ -148,7 +123,7 @@ def check_gun_pool_options do if timeout = pool_config[:await_up_timeout] do Logger.warn(""" !!!DEPRECATION WARNING!!! - Your config is using old setting name `await_up_timeout` instead of `connect_timeout`. Setting should work for now, but you are advised to change format to scheme with port to prevent possible issues later. + Your config is using old setting `config :pleroma, :connections_pool, await_up_timeout`. Please change to `config :pleroma, :connections_pool, connect_timeout` to ensure compatibility with future releases. """) Config.put(:connections_pool, Keyword.put_new(pool_config, :connect_timeout, timeout)) @@ -202,4 +177,20 @@ def check_activity_expiration_config do warning_preface ) end + + @spec check_remote_ip_plug_name() :: :ok | nil + def check_remote_ip_plug_name do + warning_preface = """ + !!!DEPRECATION WARNING!!! + Your config is using old namespace for RemoteIp Plug. Setting should work for now, but you are advised to change to new namespace to prevent possible issues later: + """ + + move_namespace_and_warn( + [ + {Pleroma.Plugs.RemoteIp, Pleroma.Web.Plugs.RemoteIp, + "\n* `config :pleroma, Pleroma.Plugs.RemoteIp` is now `config :pleroma, Pleroma.Web.Plugs.RemoteIp`"} + ], + warning_preface + ) + end end diff --git a/lib/pleroma/config/oban.ex b/lib/pleroma/config/oban.ex index 9f601b1a3..8e0351d52 100644 --- a/lib/pleroma/config/oban.ex +++ b/lib/pleroma/config/oban.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Config.Oban do require Logger diff --git a/lib/pleroma/config/config_db.ex b/lib/pleroma/config_db.ex similarity index 100% rename from lib/pleroma/config/config_db.ex rename to lib/pleroma/config_db.ex diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index e76eb0087..77933f0be 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -43,7 +43,7 @@ def get_for_ap_id(ap_id) do def maybe_create_recipientships(participation, activity) do participation = Repo.preload(participation, :recipients) - if participation.recipients |> Enum.empty?() do + if Enum.empty?(participation.recipients) do recipients = User.get_all_by_ap_id(activity.recipients) RecipientShip.create(recipients, participation) end @@ -69,10 +69,6 @@ def create_or_bump_for(activity, opts \\ []) do Enum.map(users, fn user -> invisible_conversation = Enum.any?(users, &User.blocks?(user, &1)) - unless invisible_conversation do - User.increment_unread_conversation_count(conversation, user) - end - opts = Keyword.put(opts, :invisible_conversation, invisible_conversation) {:ok, participation} = diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index 8bc3e85d6..4c32b273a 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -63,21 +63,10 @@ def mark_as_read(%User{} = user, %Conversation{} = conversation) do end end - def mark_as_read(participation) do - __MODULE__ - |> where(id: ^participation.id) - |> update(set: [read: true]) - |> select([p], p) - |> Repo.update_all([]) - |> case do - {1, [participation]} -> - participation = Repo.preload(participation, :user) - User.set_unread_conversation_count(participation.user) - {:ok, participation} - - error -> - error - end + def mark_as_read(%__MODULE__{} = participation) do + participation + |> change(read: true) + |> Repo.update() end def mark_all_as_read(%User{local: true} = user, %User{} = target_user) do @@ -93,7 +82,6 @@ def mark_all_as_read(%User{local: true} = user, %User{} = target_user) do |> update([p], set: [read: true]) |> Repo.update_all([]) - {:ok, user} = User.set_unread_conversation_count(user) {:ok, user, []} end @@ -108,7 +96,6 @@ def mark_all_as_read(%User{} = user) do |> select([p], p) |> Repo.update_all([]) - {:ok, user} = User.set_unread_conversation_count(user) {:ok, user, participations} end @@ -220,6 +207,12 @@ def set_recipients(participation, user_ids) do {:ok, Repo.preload(participation, :recipients, force: true)} end + @spec unread_count(User.t()) :: integer() + def unread_count(%User{id: user_id}) do + from(q in __MODULE__, where: q.user_id == ^user_id and q.read == false) + |> Repo.aggregate(:count, :id) + end + def unread_conversation_count_for_user(user) do from(p in __MODULE__, where: p.user_id == ^user.id, diff --git a/lib/pleroma/conversation/participation_recipient_ship.ex b/lib/pleroma/conversation/participation/recipient_ship.ex similarity index 100% rename from lib/pleroma/conversation/participation_recipient_ship.ex rename to lib/pleroma/conversation/participation/recipient_ship.ex diff --git a/lib/pleroma/docs/generator.ex b/lib/pleroma/docs/generator.ex index a671a6278..a70f83b73 100644 --- a/lib/pleroma/docs/generator.ex +++ b/lib/pleroma/docs/generator.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Docs.Generator do @callback process(keyword()) :: {:ok, String.t()} diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex index feeb4320e..13618b509 100644 --- a/lib/pleroma/docs/json.ex +++ b/lib/pleroma/docs/json.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Docs.JSON do @behaviour Pleroma.Docs.Generator @external_resource "config/description.exs" diff --git a/lib/pleroma/docs/markdown.ex b/lib/pleroma/docs/markdown.ex index da3f20f43..eac0789a6 100644 --- a/lib/pleroma/docs/markdown.ex +++ b/lib/pleroma/docs/markdown.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Docs.Markdown do @behaviour Pleroma.Docs.Generator diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex index c27ad1065..8979db2f8 100644 --- a/lib/pleroma/emails/admin_email.ex +++ b/lib/pleroma/emails/admin_email.ex @@ -88,7 +88,7 @@ def new_unapproved_registration(to, account) do html_body = """

New account for review: @#{account.nickname}

#{HTML.strip_tags(account.registration_reason)}
- Visit AdminFE + Visit AdminFE """ new() diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex index 1d8c72ae9..806a61fd2 100644 --- a/lib/pleroma/emails/user_email.ex +++ b/lib/pleroma/emails/user_email.ex @@ -189,4 +189,30 @@ def unsubscribe_url(user, notifications_type) do Router.Helpers.subscription_url(Endpoint, :unsubscribe, token) end + + def backup_is_ready_email(backup, admin_user_id \\ nil) do + %{user: user} = Pleroma.Repo.preload(backup, :user) + download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup) + + html_body = + if is_nil(admin_user_id) do + """ +

You requested a full backup of your Pleroma account. It's ready for download:

+

#{download_url}

+ """ + else + admin = Pleroma.Repo.get(User, admin_user_id) + + """ +

Admin @#{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:

+

#{download_url}

+ """ + end + + new() + |> to(recipient(user)) + |> from(sender()) + |> subject("Your account archive is ready") + |> html_body(html_body) + end end diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex index 0b3f8f00b..ca58e5432 100644 --- a/lib/pleroma/emoji/pack.ex +++ b/lib/pleroma/emoji/pack.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Emoji.Pack do @derive {Jason.Encoder, only: [:files, :pack, :files_count]} defstruct files: %{}, @@ -198,13 +202,13 @@ def import_from_filesystem do end end - @spec list_remote(String.t()) :: {:ok, map()} | {:error, atom()} - def list_remote(url) do - uri = url |> String.trim() |> URI.parse() + @spec list_remote(keyword()) :: {:ok, map()} | {:error, atom()} + def list_remote(opts) do + uri = opts[:url] |> String.trim() |> URI.parse() with :ok <- validate_shareable_packs_available(uri) do uri - |> URI.merge("/api/pleroma/emoji/packs") + |> URI.merge("/api/pleroma/emoji/packs?page=#{opts[:page]}&page_size=#{opts[:page_size]}") |> http_get() end end @@ -244,7 +248,8 @@ def download(name, url, as) do uri = url |> String.trim() |> URI.parse() with :ok <- validate_shareable_packs_available(uri), - {:ok, remote_pack} <- uri |> URI.merge("/api/pleroma/emoji/packs/#{name}") |> http_get(), + {:ok, remote_pack} <- + uri |> URI.merge("/api/pleroma/emoji/pack?name=#{name}") |> http_get(), {:ok, %{sha: sha, url: url} = pack_info} <- fetch_pack_info(remote_pack, uri, name), {:ok, archive} <- download_archive(url, sha), pack <- copy_as(remote_pack, as || name), @@ -523,7 +528,7 @@ defp get_filename(pack, shortcode) do defp http_get(%URI{} = url), do: url |> to_string() |> http_get() defp http_get(url) do - with {:ok, %{body: body}} <- url |> Pleroma.HTTP.get() do + with {:ok, %{body: body}} <- Pleroma.HTTP.get(url, [], pool: :default) do Jason.decode(body) end end @@ -572,7 +577,7 @@ defp fetch_pack_info(remote_pack, uri, name) do {:ok, %{ sha: sha, - url: URI.merge(uri, "/api/pleroma/emoji/packs/#{name}/archive") |> to_string() + url: URI.merge(uri, "/api/pleroma/emoji/packs/archive?name=#{name}") |> to_string() }} %{"fallback-src" => src, "fallback-src-sha256" => sha} when is_binary(src) -> @@ -589,7 +594,7 @@ defp fetch_pack_info(remote_pack, uri, name) do end defp download_archive(url, sha) do - with {:ok, %{body: archive}} <- Tesla.get(url) do + with {:ok, %{body: archive}} <- Pleroma.HTTP.get(url) do if Base.decode16!(sha) == :crypto.hash(:sha256, archive) do {:ok, archive} else @@ -612,7 +617,7 @@ defp fallback_sha_changed?(pack, data) do end defp update_sha_and_save_metadata(pack, data) do - with {:ok, %{body: zip}} <- Tesla.get(data[:"fallback-src"]), + with {:ok, %{body: zip}} <- Pleroma.HTTP.get(data[:"fallback-src"]), :ok <- validate_has_all_files(pack, zip) do fallback_sha = :sha256 |> :crypto.hash(zip) |> Base.encode16() diff --git a/lib/pleroma/gun/gun.ex b/lib/pleroma/gun.ex similarity index 100% rename from lib/pleroma/gun/gun.ex rename to lib/pleroma/gun.ex diff --git a/lib/pleroma/gun/connection_pool.ex b/lib/pleroma/gun/connection_pool.ex index f34602b73..e322f192a 100644 --- a/lib/pleroma/gun/connection_pool.ex +++ b/lib/pleroma/gun/connection_pool.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Gun.ConnectionPool do @registry __MODULE__ diff --git a/lib/pleroma/gun/connection_pool/reclaimer.ex b/lib/pleroma/gun/connection_pool/reclaimer.ex index cea800882..241e8b04f 100644 --- a/lib/pleroma/gun/connection_pool/reclaimer.ex +++ b/lib/pleroma/gun/connection_pool/reclaimer.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Gun.ConnectionPool.Reclaimer do use GenServer, restart: :temporary diff --git a/lib/pleroma/gun/connection_pool/worker.ex b/lib/pleroma/gun/connection_pool/worker.ex index bf57e9e5f..b71816bed 100644 --- a/lib/pleroma/gun/connection_pool/worker.ex +++ b/lib/pleroma/gun/connection_pool/worker.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Gun.ConnectionPool.Worker do alias Pleroma.Gun use GenServer, restart: :temporary diff --git a/lib/pleroma/gun/connection_pool/worker_supervisor.ex b/lib/pleroma/gun/connection_pool/worker_supervisor.ex index 39615c956..4c23bcbd9 100644 --- a/lib/pleroma/gun/connection_pool/worker_supervisor.ex +++ b/lib/pleroma/gun/connection_pool/worker_supervisor.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do @moduledoc "Supervisor for pool workers. Does not do anything except enforce max connection limit" diff --git a/lib/pleroma/helpers/inet_helper.ex b/lib/pleroma/helpers/inet_helper.ex new file mode 100644 index 000000000..126f82381 --- /dev/null +++ b/lib/pleroma/helpers/inet_helper.ex @@ -0,0 +1,19 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Helpers.InetHelper do + def parse_address(ip) when is_tuple(ip) do + {:ok, ip} + end + + def parse_address(ip) when is_binary(ip) do + ip + |> String.to_charlist() + |> parse_address() + end + + def parse_address(ip) do + :inet.parse_address(ip) + end +end diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex index b6f35a24b..6b799173e 100644 --- a/lib/pleroma/helpers/media_helper.ex +++ b/lib/pleroma/helpers/media_helper.ex @@ -9,6 +9,18 @@ defmodule Pleroma.Helpers.MediaHelper do alias Pleroma.HTTP + require Logger + + def missing_dependencies do + Enum.reduce([imagemagick: "convert", ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc -> + if Pleroma.Utils.command_available?(executable) do + acc + else + [sym | acc] + end + end) + end + def image_resize(url, options) do with executable when is_binary(executable) <- System.find_executable("convert"), {:ok, args} <- prepare_image_resize_args(options), diff --git a/lib/pleroma/http/http.ex b/lib/pleroma/http.ex similarity index 100% rename from lib/pleroma/http/http.ex rename to lib/pleroma/http.ex diff --git a/lib/pleroma/http/adapter_helper/default.ex b/lib/pleroma/http/adapter_helper/default.ex index e13441316..8567a616b 100644 --- a/lib/pleroma/http/adapter_helper/default.ex +++ b/lib/pleroma/http/adapter_helper/default.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.HTTP.AdapterHelper.Default do alias Pleroma.HTTP.AdapterHelper diff --git a/lib/pleroma/http/adapter_helper/hackney.ex b/lib/pleroma/http/adapter_helper/hackney.ex index ef84553c1..ff60513fd 100644 --- a/lib/pleroma/http/adapter_helper/hackney.ex +++ b/lib/pleroma/http/adapter_helper/hackney.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.HTTP.AdapterHelper.Hackney do @behaviour Pleroma.HTTP.AdapterHelper diff --git a/lib/pleroma/http/web_push.ex b/lib/pleroma/http/web_push.ex new file mode 100644 index 000000000..78148a12e --- /dev/null +++ b/lib/pleroma/http/web_push.ex @@ -0,0 +1,12 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTTP.WebPush do + @moduledoc false + + def post(url, payload, headers) do + list_headers = Map.to_list(headers) + Pleroma.HTTP.post(url, payload, list_headers) + end +end diff --git a/lib/pleroma/instances.ex b/lib/pleroma/instances.ex index 557e8decf..7315bd7cb 100644 --- a/lib/pleroma/instances.ex +++ b/lib/pleroma/instances.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Instances do defdelegate reachable?(url_or_host), to: @adapter defdelegate set_reachable(url_or_host), to: @adapter defdelegate set_unreachable(url_or_host, unreachable_since \\ nil), to: @adapter + defdelegate get_consistently_unreachable(), to: @adapter def set_consistently_unreachable(url_or_host), do: set_unreachable(url_or_host, reachability_datetime_threshold()) diff --git a/lib/pleroma/instances/instance.ex b/lib/pleroma/instances/instance.ex index f0f601469..df471a39d 100644 --- a/lib/pleroma/instances/instance.ex +++ b/lib/pleroma/instances/instance.ex @@ -119,6 +119,17 @@ def set_unreachable(url_or_host, unreachable_since) when is_binary(url_or_host) def set_unreachable(_, _), do: {:error, nil} + def get_consistently_unreachable do + reachability_datetime_threshold = Instances.reachability_datetime_threshold() + + from(i in Instance, + where: ^reachability_datetime_threshold > i.unreachable_since, + order_by: i.unreachable_since, + select: {i.host, i.unreachable_since} + ) + |> Repo.all() + end + defp parse_datetime(datetime) when is_binary(datetime) do NaiveDateTime.from_iso8601(datetime) end diff --git a/lib/pleroma/jwt.ex b/lib/pleroma/jwt.ex index 10102ff5d..faeb77781 100644 --- a/lib/pleroma/jwt.ex +++ b/lib/pleroma/jwt.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.JWT do use Joken.Config diff --git a/lib/pleroma/mime.ex b/lib/pleroma/mime.ex deleted file mode 100644 index 6ee055f50..000000000 --- a/lib/pleroma/mime.ex +++ /dev/null @@ -1,120 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.MIME do - @moduledoc """ - Returns the mime-type of a binary and optionally a normalized file-name. - """ - @default "application/octet-stream" - @read_bytes 35 - - @spec file_mime_type(String.t(), String.t()) :: - {:ok, content_type :: String.t(), filename :: String.t()} | {:error, any()} | :error - def file_mime_type(path, filename) do - with {:ok, content_type} <- file_mime_type(path), - filename <- fix_extension(filename, content_type) do - {:ok, content_type, filename} - end - end - - @spec file_mime_type(String.t()) :: {:ok, String.t()} | {:error, any()} | :error - def file_mime_type(filename) do - File.open(filename, [:read], fn f -> - check_mime_type(IO.binread(f, @read_bytes)) - end) - end - - def bin_mime_type(binary, filename) do - with {:ok, content_type} <- bin_mime_type(binary), - filename <- fix_extension(filename, content_type) do - {:ok, content_type, filename} - end - end - - @spec bin_mime_type(binary()) :: {:ok, String.t()} | :error - def bin_mime_type(<>) do - {:ok, check_mime_type(head)} - end - - def bin_mime_type(_), do: :error - - def mime_type(<<_::binary>>), do: {:ok, @default} - - defp fix_extension(filename, content_type) do - parts = String.split(filename, ".") - - new_filename = - if length(parts) > 1 do - Enum.drop(parts, -1) |> Enum.join(".") - else - Enum.join(parts) - end - - cond do - content_type == "application/octet-stream" -> - filename - - ext = List.first(MIME.extensions(content_type)) -> - new_filename <> "." <> ext - - true -> - Enum.join([new_filename, String.split(content_type, "/") |> List.last()], ".") - end - end - - defp check_mime_type(<<0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, _::binary>>) do - "image/png" - end - - defp check_mime_type(<<0x47, 0x49, 0x46, 0x38, _, 0x61, _::binary>>) do - "image/gif" - end - - defp check_mime_type(<<0xFF, 0xD8, 0xFF, _::binary>>) do - "image/jpeg" - end - - defp check_mime_type(<<0x1A, 0x45, 0xDF, 0xA3, _::binary>>) do - "video/webm" - end - - defp check_mime_type(<<0x00, 0x00, 0x00, _, 0x66, 0x74, 0x79, 0x70, _::binary>>) do - "video/mp4" - end - - defp check_mime_type(<<0x49, 0x44, 0x33, _::binary>>) do - "audio/mpeg" - end - - defp check_mime_type(<<255, 251, _, 68, 0, 0, 0, 0, _::binary>>) do - "audio/mpeg" - end - - defp check_mime_type( - <<0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00, _::size(160), 0x80, 0x74, 0x68, 0x65, - 0x6F, 0x72, 0x61, _::binary>> - ) do - "video/ogg" - end - - defp check_mime_type(<<0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00, _::binary>>) do - "audio/ogg" - end - - defp check_mime_type(<<"RIFF", _::binary-size(4), "WAVE", _::binary>>) do - "audio/wav" - end - - defp check_mime_type(<<"RIFF", _::binary-size(4), "WEBP", _::binary>>) do - "image/webp" - end - - defp check_mime_type(<<"RIFF", _::binary-size(4), "AVI.", _::binary>>) do - "video/avi" - end - - defp check_mime_type(_) do - @default - end -end diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index 47036a6f6..142dd8e0a 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.ModerationLog do use Ecto.Schema @@ -651,6 +655,16 @@ def get_log_entry_message(%ModerationLog{ "@#{actor_nickname} deleted chat message ##{subject_id}" end + def get_log_entry_message(%ModerationLog{ + data: %{ + "actor" => %{"nickname" => actor_nickname}, + "action" => "create_backup", + "subject" => %{"nickname" => user_nickname} + } + }) do + "@#{actor_nickname} requested account backup for @#{user_nickname}" + end + defp nicknames_to_string(nicknames) do nicknames |> Enum.map(&"@#{&1}") diff --git a/lib/pleroma/plugs/remote_ip.ex b/lib/pleroma/plugs/remote_ip.ex deleted file mode 100644 index 0ac9050d0..000000000 --- a/lib/pleroma/plugs/remote_ip.ex +++ /dev/null @@ -1,54 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.RemoteIp do - @moduledoc """ - This is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. - """ - - import Plug.Conn - - @behaviour Plug - - @headers ~w[ - x-forwarded-for - ] - - # https://en.wikipedia.org/wiki/Localhost - # https://en.wikipedia.org/wiki/Private_network - @reserved ~w[ - 127.0.0.0/8 - ::1/128 - fc00::/7 - 10.0.0.0/8 - 172.16.0.0/12 - 192.168.0.0/16 - ] - - def init(_), do: nil - - def call(%{remote_ip: original_remote_ip} = conn, _) do - config = Pleroma.Config.get(__MODULE__, []) - - if Keyword.get(config, :enabled, false) do - %{remote_ip: new_remote_ip} = conn = RemoteIp.call(conn, remote_ip_opts(config)) - assign(conn, :remote_ip_found, original_remote_ip != new_remote_ip) - else - conn - end - end - - defp remote_ip_opts(config) do - headers = config |> Keyword.get(:headers, @headers) |> MapSet.new() - reserved = Keyword.get(config, :reserved, @reserved) - - proxies = - config - |> Keyword.get(:proxies, []) - |> Enum.concat(reserved) - |> Enum.map(&InetCidr.parse/1) - - {headers, proxies} - end -end diff --git a/lib/pleroma/reverse_proxy/reverse_proxy.ex b/lib/pleroma/reverse_proxy.ex similarity index 100% rename from lib/pleroma/reverse_proxy/reverse_proxy.ex rename to lib/pleroma/reverse_proxy.ex diff --git a/lib/pleroma/telemetry/logger.ex b/lib/pleroma/telemetry/logger.ex index 197b1d091..003079cf3 100644 --- a/lib/pleroma/telemetry/logger.ex +++ b/lib/pleroma/telemetry/logger.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Telemetry.Logger do @moduledoc "Transforms Pleroma telemetry events to logs" diff --git a/lib/pleroma/tests/auth_test_controller.ex b/lib/pleroma/tests/auth_test_controller.ex index fb04411d9..b30d83567 100644 --- a/lib/pleroma/tests/auth_test_controller.ex +++ b/lib/pleroma/tests/auth_test_controller.ex @@ -8,9 +8,9 @@ defmodule Pleroma.Tests.AuthTestController do use Pleroma.Web, :controller - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User + alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug # Serves only with proper OAuth token (:api and :authenticated_api) # Skipping EnsurePublicOrAuthenticatedPlug has no effect in this case diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 015c87593..db2cc1dae 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -66,6 +66,7 @@ defp get_description(opts, upload) do end @spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()} + @doc "Store a file. If using a `Plug.Upload{}` as the source, be sure to use `Majic.Plug` to ensure its content_type and filename is correct." def store(upload, opts \\ []) do opts = get_opts(opts) @@ -139,14 +140,13 @@ defp get_opts(opts) do end defp prepare_upload(%Plug.Upload{} = file, opts) do - with :ok <- check_file_size(file.path, opts.size_limit), - {:ok, content_type, name} <- Pleroma.MIME.file_mime_type(file.path, file.filename) do + with :ok <- check_file_size(file.path, opts.size_limit) do {:ok, %__MODULE__{ id: UUID.generate(), - name: name, + name: file.filename, tempfile: file.path, - content_type: content_type + content_type: file.content_type }} end end @@ -154,16 +154,17 @@ defp prepare_upload(%Plug.Upload{} = file, opts) do defp prepare_upload(%{img: "data:image/" <> image_data}, opts) do parsed = Regex.named_captures(~r/(?jpeg|png|gif);base64,(?.*)/, image_data) data = Base.decode64!(parsed["data"], ignore: :whitespace) - hash = String.downcase(Base.encode16(:crypto.hash(:sha256, data))) + hash = Base.encode16(:crypto.hash(:sha256, data), lower: true) with :ok <- check_binary_size(data, opts.size_limit), tmp_path <- tempfile_for_image(data), - {:ok, content_type, name} <- - Pleroma.MIME.bin_mime_type(data, hash <> "." <> parsed["filetype"]) do + {:ok, %{mime_type: content_type}} <- + Majic.perform({:bytes, data}, pool: Pleroma.MajicPool), + [ext | _] <- MIME.extensions(content_type) do {:ok, %__MODULE__{ id: UUID.generate(), - name: name, + name: hash <> "." <> ext, tempfile: tmp_path, content_type: content_type }} @@ -172,7 +173,7 @@ defp prepare_upload(%{img: "data:image/" <> image_data}, opts) do # For Mix.Tasks.MigrateLocalUploads defp prepare_upload(%__MODULE__{tempfile: path} = upload, _opts) do - with {:ok, content_type} <- Pleroma.MIME.file_mime_type(path) do + with {:ok, %{mime_type: content_type}} <- Majic.perform(path, pool: Pleroma.MajicPool) do {:ok, %__MODULE__{upload | content_type: content_type}} end end diff --git a/lib/pleroma/uploaders/uploader.ex b/lib/pleroma/uploaders/uploader.ex index 9a94534e9..6249eceb1 100644 --- a/lib/pleroma/uploaders/uploader.ex +++ b/lib/pleroma/uploaders/uploader.ex @@ -12,7 +12,7 @@ defmodule Pleroma.Uploaders.Uploader do @doc """ Instructs how to get the file from the backend. - Used by `Pleroma.Plugs.UploadedMedia`. + Used by `Pleroma.Web.Plugs.UploadedMedia`. """ @type get_method :: {:static_dir, directory :: String.t()} | {:url, url :: String.t()} @callback get_file(file :: String.t()) :: {:ok, get_method()} diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 5dad7285a..8e4ec8064 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -107,7 +107,7 @@ defmodule Pleroma.User do field(:note_count, :integer, default: 0) field(:follower_count, :integer, default: 0) field(:following_count, :integer, default: 0) - field(:locked, :boolean, default: false) + field(:is_locked, :boolean, default: false) field(:confirmation_pending, :boolean, default: false) field(:password_reset_pending, :boolean, default: false) field(:approval_pending, :boolean, default: false) @@ -128,7 +128,6 @@ defmodule Pleroma.User do field(:hide_followers, :boolean, default: false) field(:hide_follows, :boolean, default: false) field(:hide_favorites, :boolean, default: true) - field(:unread_conversation_count, :integer, default: 0) field(:pinned_activities, {:array, :string}, default: []) field(:email_notifications, :map, default: %{"digest" => false}) field(:mascot, :map, default: nil) @@ -136,7 +135,7 @@ defmodule Pleroma.User do field(:pleroma_settings_store, :map, default: %{}) field(:fields, {:array, :map}, default: []) field(:raw_fields, {:array, :map}, default: []) - field(:discoverable, :boolean, default: false) + field(:is_discoverable, :boolean, default: false) field(:invisible, :boolean, default: false) field(:allow_following_move, :boolean, default: true) field(:skip_thread_containment, :boolean, default: false) @@ -426,7 +425,6 @@ def remote_user_changeset(struct \\ %User{local: false}, params) do params, [ :bio, - :name, :emoji, :ap_id, :inbox, @@ -436,7 +434,7 @@ def remote_user_changeset(struct \\ %User{local: false}, params) do :avatar, :ap_enabled, :banner, - :locked, + :is_locked, :last_refreshed_at, :uri, :follower_address, @@ -448,14 +446,16 @@ def remote_user_changeset(struct \\ %User{local: false}, params) do :follower_count, :fields, :following_count, - :discoverable, + :is_discoverable, :invisible, :actor_type, :also_known_as, :accepts_chat_messages ] ) - |> validate_required([:name, :ap_id]) + |> cast(params, [:name], empty_values: []) + |> validate_required([:ap_id]) + |> validate_required([:name], trim: false) |> unique_constraint(:nickname) |> validate_format(:nickname, @email_regex) |> validate_length(:bio, max: bio_limit) @@ -479,7 +479,7 @@ def update_changeset(struct, params \\ %{}) do :public_key, :inbox, :shared_inbox, - :locked, + :is_locked, :no_rich_text, :default_scope, :banner, @@ -495,7 +495,7 @@ def update_changeset(struct, params \\ %{}) do :fields, :raw_fields, :pleroma_settings_store, - :discoverable, + :is_discoverable, :actor_type, :also_known_as, :accepts_chat_messages @@ -765,6 +765,16 @@ defp autofollow_users(user) do follow_all(user, autofollowed_users) end + defp autofollowing_users(user) do + candidates = Config.get([:instance, :autofollowing_nicknames]) + + User.Query.build(%{nickname: candidates, local: true, deactivated: false}) + |> Repo.all() + |> Enum.each(&follow(&1, user, :follow_accept)) + + {:ok, :success} + end + @doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)" def register(%Ecto.Changeset{} = changeset) do with {:ok, user} <- Repo.insert(changeset) do @@ -774,6 +784,7 @@ def register(%Ecto.Changeset{} = changeset) do 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), @@ -813,7 +824,8 @@ def send_welcome_email(%User{email: email} = user) when is_binary(email) do def 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} = user) do + def try_send_confirmation_email(%User{confirmation_pending: true, email: email} = user) + when is_binary(email) do if Config.get([:instance, :account_activation_required]) do send_confirmation_email(user) {:ok, :enqueued} @@ -846,7 +858,7 @@ def needs_update?(_), do: true @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()} # "Locked" (self-locked) users demand explicit authorization of follow requests - def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do + def maybe_direct_follow(%User{} = follower, %User{local: true, is_locked: true} = followed) do follow(follower, followed, :follow_pending) end @@ -914,9 +926,7 @@ defp do_unfollow(%User{} = follower, %User{} = followed) do FollowingRelationship.unfollow(follower, followed) {:ok, followed} = update_follower_count(followed) - {:ok, follower} = - follower - |> update_following_count() + {:ok, follower} = update_following_count(follower) {:ok, follower, followed} @@ -955,7 +965,7 @@ def get_follow_state( end def locked?(%User{} = user) do - user.locked || false + user.is_locked || false end def get_by_id(id) do @@ -1294,47 +1304,6 @@ def update_following_count(%User{local: true} = user) do |> update_and_set_cache() end - def set_unread_conversation_count(%User{local: true} = user) do - unread_query = Participation.unread_conversation_count_for_user(user) - - User - |> join(:inner, [u], p in subquery(unread_query)) - |> update([u, p], - set: [unread_conversation_count: p.count] - ) - |> where([u], u.id == ^user.id) - |> select([u], u) - |> Repo.update_all([]) - |> case do - {1, [user]} -> set_cache(user) - _ -> {:error, user} - end - end - - def set_unread_conversation_count(user), do: {:ok, user} - - def increment_unread_conversation_count(conversation, %User{local: true} = user) do - unread_query = - Participation.unread_conversation_count_for_user(user) - |> where([p], p.conversation_id == ^conversation.id) - - User - |> join(:inner, [u], p in subquery(unread_query)) - |> update([u, p], - inc: [unread_conversation_count: 1] - ) - |> where([u], u.id == ^user.id) - |> where([u, p], p.count == 0) - |> select([u], u) - |> Repo.update_all([]) - |> case do - {1, [user]} -> set_cache(user) - _ -> {:error, user} - end - end - - def increment_unread_conversation_count(_, user), do: {:ok, user} - @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) @@ -1636,7 +1605,7 @@ def purge_user_changeset(user) do note_count: 0, follower_count: 0, following_count: 0, - locked: false, + is_locked: false, confirmation_pending: false, password_reset_pending: false, approval_pending: false, @@ -1653,7 +1622,7 @@ def purge_user_changeset(user) do pleroma_settings_store: %{}, fields: [], raw_fields: [], - discoverable: false, + is_discoverable: false, also_known_as: [] }) end @@ -2105,6 +2074,13 @@ 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) + |> update_and_set_cache() + end + def get_mascot(%{mascot: %{} = mascot}) when not is_nil(mascot) do mascot end @@ -2319,7 +2295,9 @@ def remove_pinnned_activity(user, %Pleroma.Activity{id: id, data: data}) do # if pinned activity was scheduled for deletion, we reschedule it for deletion if data["expires_at"] do - {:ok, expires_at, _} = DateTime.from_iso8601(data["expires_at"]) + # 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, diff --git a/lib/pleroma/user/backup.ex b/lib/pleroma/user/backup.ex new file mode 100644 index 000000000..a9041fd94 --- /dev/null +++ b/lib/pleroma/user/backup.ex @@ -0,0 +1,258 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.Backup do + use Ecto.Schema + + import Ecto.Changeset + import Ecto.Query + import Pleroma.Web.Gettext + + require Pleroma.Constants + + alias Pleroma.Activity + alias Pleroma.Bookmark + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.ActivityPub.UserView + alias Pleroma.Workers.BackupWorker + + schema "backups" do + field(:content_type, :string) + field(:file_name, :string) + field(:file_size, :integer, default: 0) + field(:processed, :boolean, default: false) + + belongs_to(:user, User, type: FlakeId.Ecto.CompatType) + + timestamps() + end + + def create(user, admin_id \\ nil) do + with :ok <- validate_email_enabled(), + :ok <- validate_user_email(user), + :ok <- validate_limit(user, admin_id), + {:ok, backup} <- user |> new() |> Repo.insert() do + BackupWorker.process(backup, admin_id) + end + end + + def new(user) do + rand_str = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false) + datetime = Calendar.NaiveDateTime.Format.iso8601_basic(NaiveDateTime.utc_now()) + name = "archive-#{user.nickname}-#{datetime}-#{rand_str}.zip" + + %__MODULE__{ + user_id: user.id, + content_type: "application/zip", + file_name: name + } + end + + def delete(backup) do + uploader = Pleroma.Config.get([Pleroma.Upload, :uploader]) + + with :ok <- uploader.delete_file(Path.join("backups", backup.file_name)) do + Repo.delete(backup) + end + end + + defp validate_limit(_user, admin_id) when is_binary(admin_id), do: :ok + + defp validate_limit(user, nil) do + case get_last(user.id) do + %__MODULE__{inserted_at: inserted_at} -> + days = Pleroma.Config.get([__MODULE__, :limit_days]) + diff = Timex.diff(NaiveDateTime.utc_now(), inserted_at, :days) + + if diff > days do + :ok + else + {:error, + dngettext( + "errors", + "Last export was less than a day ago", + "Last export was less than %{days} days ago", + days, + days: days + )} + end + + nil -> + :ok + end + end + + defp validate_email_enabled do + if Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) do + :ok + else + {:error, dgettext("errors", "Backups require enabled email")} + end + end + + defp validate_user_email(%User{email: nil}) do + {:error, dgettext("errors", "Email is required")} + end + + defp validate_user_email(%User{email: email}) when is_binary(email), do: :ok + + def get_last(user_id) do + __MODULE__ + |> where(user_id: ^user_id) + |> order_by(desc: :id) + |> limit(1) + |> Repo.one() + end + + def list(%User{id: user_id}) do + __MODULE__ + |> where(user_id: ^user_id) + |> order_by(desc: :id) + |> Repo.all() + end + + def remove_outdated(%__MODULE__{id: latest_id, user_id: user_id}) do + __MODULE__ + |> where(user_id: ^user_id) + |> where([b], b.id != ^latest_id) + |> Repo.all() + |> Enum.each(&BackupWorker.delete/1) + end + + def get(id), do: Repo.get(__MODULE__, id) + + def process(%__MODULE__{} = backup) do + with {:ok, zip_file} <- export(backup), + {:ok, %{size: size}} <- File.stat(zip_file), + {:ok, _upload} <- upload(backup, zip_file) do + backup + |> cast(%{file_size: size, processed: true}, [:file_size, :processed]) + |> Repo.update() + end + end + + @files ['actor.json', 'outbox.json', 'likes.json', 'bookmarks.json'] + def export(%__MODULE__{} = backup) do + backup = Repo.preload(backup, :user) + name = String.trim_trailing(backup.file_name, ".zip") + dir = dir(name) + + with :ok <- File.mkdir(dir), + :ok <- actor(dir, backup.user), + :ok <- statuses(dir, backup.user), + :ok <- likes(dir, backup.user), + :ok <- bookmarks(dir, backup.user), + {:ok, zip_path} <- :zip.create(String.to_charlist(dir <> ".zip"), @files, cwd: dir), + {:ok, _} <- File.rm_rf(dir) do + {:ok, to_string(zip_path)} + end + end + + def dir(name) do + dir = Pleroma.Config.get([__MODULE__, :dir]) || System.tmp_dir!() + Path.join(dir, name) + end + + def upload(%__MODULE__{} = backup, zip_path) do + uploader = Pleroma.Config.get([Pleroma.Upload, :uploader]) + + upload = %Pleroma.Upload{ + name: backup.file_name, + tempfile: zip_path, + content_type: backup.content_type, + path: Path.join("backups", backup.file_name) + } + + with {:ok, _} <- Pleroma.Uploaders.Uploader.put_file(uploader, upload), + :ok <- File.rm(zip_path) do + {:ok, upload} + end + end + + defp actor(dir, user) do + with {:ok, json} <- + UserView.render("user.json", %{user: user}) + |> Map.merge(%{"likes" => "likes.json", "bookmarks" => "bookmarks.json"}) + |> Jason.encode() do + File.write(Path.join(dir, "actor.json"), json) + end + end + + defp write_header(file, name) do + IO.write( + file, + """ + { + "@context": "https://www.w3.org/ns/activitystreams", + "id": "#{name}.json", + "type": "OrderedCollection", + "orderedItems": [ + + """ + ) + end + + defp write(query, dir, name, fun) do + path = Path.join(dir, "#{name}.json") + + with {:ok, file} <- File.open(path, [:write, :utf8]), + :ok <- write_header(file, name) do + total = + query + |> Pleroma.Repo.chunk_stream(100) + |> Enum.reduce(0, fn i, acc -> + with {:ok, data} <- fun.(i), + {:ok, str} <- Jason.encode(data), + :ok <- IO.write(file, str <> ",\n") do + acc + 1 + else + _ -> acc + end + end) + + with :ok <- :file.pwrite(file, {:eof, -2}, "\n],\n \"totalItems\": #{total}}") do + File.close(file) + end + end + end + + defp bookmarks(dir, %{id: user_id} = _user) do + Bookmark + |> where(user_id: ^user_id) + |> join(:inner, [b], activity in assoc(b, :activity)) + |> select([b, a], %{id: b.id, object: fragment("(?)->>'object'", a.data)}) + |> write(dir, "bookmarks", fn a -> {:ok, a.object} end) + end + + defp likes(dir, user) do + user.ap_id + |> Activity.Queries.by_actor() + |> Activity.Queries.by_type("Like") + |> select([like], %{id: like.id, object: fragment("(?)->>'object'", like.data)}) + |> write(dir, "likes", fn a -> {:ok, a.object} end) + end + + defp statuses(dir, user) do + opts = + %{} + |> Map.put(:type, ["Create", "Announce"]) + |> Map.put(:actor_id, user.ap_id) + + [ + [Pleroma.Constants.as_public(), user.ap_id], + User.following(user), + Pleroma.List.memberships(user) + ] + |> Enum.concat() + |> ActivityPub.fetch_activities_query(opts) + |> write(dir, "outbox", fn a -> + with {:ok, activity} <- Transmogrifier.prepare_outgoing(a.data) do + {:ok, Map.delete(activity, "@context")} + end + end) + end +end diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index d618432ff..7ef2a1455 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -43,10 +43,12 @@ defmodule Pleroma.User.Query do active: boolean(), deactivated: boolean(), need_approval: boolean(), + unconfirmed: boolean(), is_admin: boolean(), is_moderator: boolean(), super_users: boolean(), invisible: boolean(), + internal: boolean(), followers: User.t(), friends: User.t(), recipients_from_activity: [String.t()], @@ -54,7 +56,8 @@ defmodule Pleroma.User.Query do ap_id: [String.t()], order_by: term(), select: term(), - limit: pos_integer() + limit: pos_integer(), + actor_types: [String.t()] } | map() @@ -80,7 +83,9 @@ defp base_query do end defp prepare_query(query, criteria) do - Enum.reduce(criteria, query, &compose_query/2) + criteria + |> Map.put_new(:internal, false) + |> Enum.reduce(query, &compose_query/2) end defp compose_query({key, value}, query) @@ -107,12 +112,16 @@ defp compose_query({:tags, tags}, query) when is_list(tags) and length(tags) > 0 where(query, [u], fragment("? && ?", u.tags, ^tags)) end - defp compose_query({:is_admin, _}, query) do - where(query, [u], u.is_admin) + defp compose_query({:is_admin, bool}, query) do + where(query, [u], u.is_admin == ^bool) end - defp compose_query({:is_moderator, _}, query) do - where(query, [u], u.is_moderator) + defp compose_query({:actor_types, actor_types}, query) when is_list(actor_types) do + where(query, [u], u.actor_type in ^actor_types) + end + + defp compose_query({:is_moderator, bool}, query) do + where(query, [u], u.is_moderator == ^bool) end defp compose_query({:super_users, _}, query) do @@ -129,14 +138,12 @@ defp compose_query({:external, _}, query), do: location_query(query, false) defp compose_query({:active, _}, query) do User.restrict_deactivated(query) - |> where([u], not is_nil(u.nickname)) |> where([u], u.approval_pending == false) end defp compose_query({:legacy_active, _}, query) do query |> where([u], fragment("not (?->'deactivated' @> 'true')", u.info)) - |> where([u], not is_nil(u.nickname)) end defp compose_query({:deactivated, false}, query) do @@ -145,13 +152,20 @@ defp compose_query({:deactivated, false}, query) do defp compose_query({:deactivated, true}, query) do where(query, [u], u.deactivated == ^true) - |> where([u], not is_nil(u.nickname)) + end + + defp compose_query({:confirmation_pending, bool}, query) do + where(query, [u], u.confirmation_pending == ^bool) end defp compose_query({:need_approval, _}, query) do where(query, [u], u.approval_pending) end + defp compose_query({:unconfirmed, _}, query) do + where(query, [u], u.confirmation_pending) + end + defp compose_query({:followers, %User{id: id}}, query) do query |> where([u], u.id != ^id) @@ -199,10 +213,15 @@ defp compose_query({:limit, limit}, query) do limit(query, ^limit) end + defp compose_query({:internal, false}, query) do + query + |> where([u], not is_nil(u.nickname)) + |> where([u], not like(u.nickname, "internal.%")) + end + defp compose_query(_unsupported_param, query), do: query defp location_query(query, local) do where(query, [u], u.local == ^local) - |> where([u], not is_nil(u.nickname)) end end diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex index b8c648672..2dab67211 100644 --- a/lib/pleroma/user/search.ex +++ b/lib/pleroma/user/search.ex @@ -3,8 +3,10 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.Search do + alias Pleroma.EctoType.ActivityPub.ObjectValidators.Uri, as: UriType alias Pleroma.Pagination alias Pleroma.User + import Ecto.Query @limit 20 @@ -19,16 +21,47 @@ def search(query_string, opts \\ []) do query_string = format_query(query_string) - maybe_resolve(resolve, for_user, query_string) + # If this returns anything, it should bounce to the top + maybe_resolved = maybe_resolve(resolve, for_user, query_string) + + top_user_ids = + [] + |> maybe_add_resolved(maybe_resolved) + |> maybe_add_ap_id_match(query_string) + |> maybe_add_uri_match(query_string) results = query_string - |> search_query(for_user, following) + |> search_query(for_user, following, top_user_ids) |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset) results end + defp maybe_add_resolved(list, {:ok, %User{} = user}) do + [user.id | list] + end + + defp maybe_add_resolved(list, _), do: list + + defp maybe_add_ap_id_match(list, query) do + if user = User.get_cached_by_ap_id(query) do + [user.id | list] + else + list + end + end + + defp maybe_add_uri_match(list, query) do + with {:ok, query} <- UriType.cast(query), + q = from(u in User, where: u.uri == ^query, select: u.id), + users = Pleroma.Repo.all(q) do + users ++ list + else + _ -> list + end + end + defp format_query(query_string) do # Strip the beginning @ off if there is a query query_string = String.trim_leading(query_string, "@") @@ -47,7 +80,7 @@ defp format_query(query_string) do end end - defp search_query(query_string, for_user, following) do + defp search_query(query_string, for_user, following, top_user_ids) do for_user |> base_query(following) |> filter_blocked_user(for_user) @@ -56,13 +89,20 @@ defp search_query(query_string, for_user, following) do |> filter_internal_users() |> filter_blocked_domains(for_user) |> fts_search(query_string) + |> select_top_users(top_user_ids) |> trigram_rank(query_string) - |> boost_search_rank(for_user) + |> boost_search_rank(for_user, top_user_ids) |> subquery() |> order_by(desc: :search_rank) |> maybe_restrict_local(for_user) end + defp select_top_users(query, top_user_ids) do + from(u in query, + or_where: u.id in ^top_user_ids + ) + end + defp fts_search(query, query_string) do query_string = to_tsquery(query_string) @@ -124,7 +164,7 @@ defp filter_invisible_users(query) do end defp filter_discoverable_users(query) do - from(q in query, where: q.discoverable == true) + from(q in query, where: q.is_discoverable == true) end defp filter_internal_users(query) do @@ -180,7 +220,7 @@ defp restrict_local(q), do: where(q, [u], u.local == true) defp local_domain, do: Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host]) - defp boost_search_rank(query, %User{} = for_user) do + defp boost_search_rank(query, %User{} = for_user, top_user_ids) do friends_ids = User.get_friends_ids(for_user) followers_ids = User.get_followers_ids(for_user) @@ -192,6 +232,7 @@ defp boost_search_rank(query, %User{} = for_user) do CASE WHEN (?) THEN (?) * 1.5 WHEN (?) THEN (?) * 1.3 WHEN (?) THEN (?) * 1.1 + WHEN (?) THEN 9001 ELSE (?) END """, u.id in ^friends_ids and u.id in ^followers_ids, @@ -200,11 +241,26 @@ defp boost_search_rank(query, %User{} = for_user) do u.search_rank, u.id in ^followers_ids, u.search_rank, + u.id in ^top_user_ids, u.search_rank ) } ) end - defp boost_search_rank(query, _for_user), do: query + defp boost_search_rank(query, _for_user, top_user_ids) do + from(u in subquery(query), + select_merge: %{ + search_rank: + fragment( + """ + CASE WHEN (?) THEN 9001 + ELSE (?) END + """, + u.id in ^top_user_ids, + u.search_rank + ) + } + ) + end end diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web.ex similarity index 93% rename from lib/pleroma/web/web.ex rename to lib/pleroma/web.ex index 4f9281851..6ed19d3dd 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web.ex @@ -2,11 +2,6 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.Plug do - # Substitute for `call/2` which is defined with `use Pleroma.Web, :plug` - @callback perform(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t() -end - defmodule Pleroma.Web do @moduledoc """ A module that keeps using definitions for controllers, @@ -25,12 +20,12 @@ defmodule Pleroma.Web do below. """ - alias Pleroma.Plugs.EnsureAuthenticatedPlug - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug - alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.PlugHelper + alias Pleroma.Web.Plugs.EnsureAuthenticatedPlug + alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug + alias Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.PlugHelper def controller do quote do @@ -177,7 +172,7 @@ def router do def channel do quote do # credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse - use Phoenix.Channel + import Phoenix.Channel import Pleroma.Web.Gettext end end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index aacd58d03..d8f685d38 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -790,7 +790,17 @@ defp restrict_replies(query, %{ [activity, object] in query, where: fragment( - "?->>'inReplyTo' is null OR ? && array_remove(?, ?) OR ? = ?", + """ + ?->>'type' != 'Create' -- This isn't a Create + OR ?->>'inReplyTo' is null -- this isn't a reply + OR ? && array_remove(?, ?) -- The recipient is us or one of our friends, + -- unless they are the author (because authors + -- are also part of the recipients). This leads + -- to a bug that self-replies by friends won't + -- show up. + OR ? = ? -- The actor is us + """, + activity.data, object.data, ^[user.ap_id | User.get_cached_user_friends_ap_ids(user)], activity.recipients, @@ -817,7 +827,14 @@ defp restrict_muted(query, %{muting_user: %User{} = user} = opts) do query = from([activity] in query, where: fragment("not (? = ANY(?))", activity.actor, ^mutes), - where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes) + where: + fragment( + "not (?->'to' \\?| ?) or ? = ?", + activity.data, + ^mutes, + activity.actor, + ^user.ap_id + ) ) unless opts[:skip_preload] do @@ -920,16 +937,11 @@ defp restrict_muted_reblogs(query, %{muting_user: %User{} = user} = opts) do defp restrict_muted_reblogs(query, _), do: query - defp restrict_instance(query, %{instance: instance}) do - users = - from( - u in User, - select: u.ap_id, - where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}") - ) - |> Repo.all() - - from(activity in query, where: activity.actor in ^users) + defp restrict_instance(query, %{instance: instance}) when is_binary(instance) do + from( + activity in query, + where: fragment("split_part(actor::text, '/'::text, 3) = ?", ^instance) + ) end defp restrict_instance(query, _), do: query @@ -1218,11 +1230,11 @@ defp object_to_user_data(data) do {String.trim(name, ":"), url} end) - locked = data["manuallyApprovesFollowers"] || false + is_locked = data["manuallyApprovesFollowers"] || false capabilities = data["capabilities"] || %{} accepts_chat_messages = capabilities["acceptsChatMessages"] data = Transmogrifier.maybe_fix_user_object(data) - discoverable = data["discoverable"] || false + is_discoverable = data["discoverable"] || false invisible = data["invisible"] || false actor_type = data["type"] || "Person" @@ -1247,8 +1259,8 @@ defp object_to_user_data(data) do banner: banner, fields: fields, emoji: emojis, - locked: locked, - discoverable: discoverable, + is_locked: is_locked, + is_discoverable: is_discoverable, invisible: invisible, avatar: avatar, name: data["name"], @@ -1361,6 +1373,7 @@ def fetch_and_prepare_user_from_ap_id(ap_id, opts \\ []) do {:ok, data} <- user_data_from_user_object(data) do {:ok, maybe_update_follow_information(data)} else + # If this has been deleted, only log a debug and not an error {:error, "Object has been deleted" = e} -> Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}") {:error, e} diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 732c44271..31df80adb 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -9,7 +9,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do alias Pleroma.Delivery alias Pleroma.Object alias Pleroma.Object.Fetcher - alias Pleroma.Plugs.EnsureAuthenticatedPlug alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Builder @@ -23,8 +22,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.ControllerHelper alias Pleroma.Web.Endpoint - alias Pleroma.Web.FederatingPlug alias Pleroma.Web.Federator + alias Pleroma.Web.Plugs.EnsureAuthenticatedPlug + alias Pleroma.Web.Plugs.FederatingPlug require Logger @@ -45,8 +45,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do when action in [:read_inbox, :update_outbox, :whoami, :upload_media] ) + plug(Majic.Plug, [pool: Pleroma.MajicPool] when action in [:upload_media]) + plug( - Pleroma.Plugs.Cache, + Pleroma.Web.Plugs.Cache, [query_params: false, tracking_fun: &__MODULE__.track_object_fetch/2] when action in [:activity, :object] ) @@ -412,7 +414,7 @@ defp handle_user_activity( object = object |> Map.merge(Map.take(params, ["to", "cc"])) - |> Map.put("attributedTo", user.ap_id()) + |> Map.put("attributedTo", user.ap_id) |> Transmogrifier.fix_object() ActivityPub.create(%{ @@ -456,7 +458,7 @@ def update_outbox( %{assigns: %{user: %User{nickname: nickname} = user}} = conn, %{"nickname" => nickname} = params ) do - actor = user.ap_id() + actor = user.ap_id params = params @@ -523,19 +525,6 @@ defp ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do {new_user, for_user} end - @doc """ - Endpoint based on - - Parameters: - - (required) `file`: data of the media - - (optionnal) `description`: description of the media, intended for accessibility - - Response: - - HTTP Code: 201 Created - - HTTP Body: ActivityPub object to be inserted into another's `attachment` field - - Note: Will not point to a URL with a `Location` header because no standalone Activity has been created. - """ def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do with {:ok, object} <- ActivityPub.upload( diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index 9a7b7d9de..298aff6b7 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.Builder do @moduledoc """ This module builds the objects. Meant to be used for creating local objects. diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 9c3956683..a2930c1cd 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -242,9 +242,7 @@ def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity) end) end - @doc """ - Publishes an activity to all relevant peers. - """ + # Publishes an activity to all relevant peers. def publish(%User{} = actor, %Activity{} = activity) do public = is_public?(activity) diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index b65710a94..6606e1780 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -30,12 +30,16 @@ def follow(target_instance) do end end - @spec unfollow(String.t()) :: {:ok, Activity.t()} | {:error, any()} - def unfollow(target_instance) do + @spec unfollow(String.t(), map()) :: {:ok, Activity.t()} | {:error, any()} + def unfollow(target_instance, opts \\ %{}) do with %User{} = local_user <- get_actor(), - {:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_instance), + {:ok, target_user} <- fetch_target_user(target_instance, opts), {:ok, activity} <- ActivityPub.unfollow(local_user, target_user) do - User.unfollow(local_user, target_user) + case target_user.id do + nil -> User.update_following_count(local_user) + _ -> User.unfollow(local_user, target_user) + end + Logger.info("relay: unfollowed instance: #{target_instance}: id=#{activity.data["id"]}") {:ok, activity} else @@ -43,6 +47,14 @@ def unfollow(target_instance) do end end + defp fetch_target_user(ap_id, opts) do + case {opts[:force], User.get_or_fetch_by_ap_id(ap_id)} do + {_, {:ok, %User{} = user}} -> {:ok, user} + {true, _} -> {:ok, %User{ap_id: ap_id}} + {_, error} -> error + end + end + @spec publish(any()) :: {:ok, Activity.t()} | {:error, any()} def publish(%Activity{data: %{"type" => "Create"}} = activity) do with %User{} = user <- get_actor(), diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index b9a83a544..bbff35c36 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.SideEffects do @moduledoc """ This module looks at an inserted object and executes the side effects that it @@ -98,7 +102,7 @@ def handle( %User{} = followed <- User.get_cached_by_ap_id(followed_user), {_, {:ok, _}, _, _} <- {:following, User.follow(follower, followed, :follow_pending), follower, followed} do - if followed.local && !followed.locked do + if followed.local && !followed.is_locked do {:ok, accept_data, _} = Builder.accept(followed, object) {:ok, _activity, _} = Pipeline.common_pipeline(accept_data, local: true) end @@ -183,7 +187,7 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do {:ok, notifications} = Notification.create_notifications(activity, do_send: false) {:ok, _user} = ActivityPub.increase_note_count_if_public(user, object) - if in_reply_to = object.data["inReplyTo"] do + if in_reply_to = object.data["inReplyTo"] && object.data["type"] != "Answer" do Object.increase_replies_count(in_reply_to) end @@ -302,11 +306,18 @@ def handle_object_creation(%{"type" => "ChatMessage"} = object, meta) do streamables = [[actor, recipient], [recipient, actor]] + |> Enum.uniq() |> Enum.map(fn [user, other_user] -> if user.local 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( + :chat_message_id_idempotency_key_cache, + cm_ref.id, + meta[:idempotency_key] + ) + { ["user", "user:pleroma_chat"], {user, %{cm_ref | chat: chat, object: object}} diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index aa6a69463..39c8f7e39 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -40,6 +40,7 @@ def fix_object(object, options \\ []) do |> fix_in_reply_to(options) |> fix_emoji |> fix_tag + |> set_sensitive |> fix_content_map |> fix_addressing |> fix_summary @@ -313,19 +314,21 @@ 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 data -> String.slice(data["name"], 1..-1) end) + |> Enum.map(fn %{"name" => name} -> + name + |> String.slice(1..-1) + |> String.downcase() + end) Map.put(object, "tag", tag ++ tags) end - def fix_tag(%{"tag" => %{"type" => "Hashtag", "name" => hashtag} = tag} = object) do - combined = [tag, String.slice(hashtag, 1..-1)] - - Map.put(object, "tag", combined) + def fix_tag(%{"tag" => %{} = tag} = object) do + object + |> Map.put("tag", [tag]) + |> fix_tag end - def fix_tag(%{"tag" => %{} = tag} = object), do: Map.put(object, "tag", [tag]) - def fix_tag(object), do: object # content map usually only has one language so this will do for now. @@ -515,15 +518,19 @@ def handle_incoming( end def handle_incoming( - %{"type" => "Create", "object" => %{"type" => objtype}} = data, + %{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data, _options ) when objtype in ~w{Question Answer ChatMessage Audio Video Event Article} do data = Map.put(data, "object", strip_internal_fields(data["object"])) with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), + nil <- Activity.get_create_by_object_ap_id(obj_id), {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do {:ok, activity} + else + %Activity{} = activity -> {:ok, activity} + e -> e end end @@ -923,7 +930,7 @@ def set_conversation(object) do Map.put(object, "conversation", object["context"]) end - def set_sensitive(%{"sensitive" => true} = object) do + def set_sensitive(%{"sensitive" => _} = object) do object end diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 3a4564912..4dc45cde3 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -101,7 +101,7 @@ def render("user.json", %{user: user}) do "name" => user.name, "summary" => user.bio, "url" => user.ap_id, - "manuallyApprovesFollowers" => user.locked, + "manuallyApprovesFollowers" => user.is_locked, "publicKey" => %{ "id" => "#{user.ap_id}#main-key", "owner" => user.ap_id, @@ -110,7 +110,7 @@ def render("user.json", %{user: user}) do "endpoints" => endpoints, "attachment" => fields, "tag" => emoji_tags, - "discoverable" => user.discoverable, + "discoverable" => user.is_discoverable, "capabilities" => capabilities } |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user)) diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex index 5c349bb7a..76bd54a42 100644 --- a/lib/pleroma/web/activity_pub/visibility.ex +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -44,29 +44,30 @@ def is_direct?(activity) do def is_list?(%{data: %{"listMessage" => _}}), do: true def is_list?(_), do: false - @spec visible_for_user?(Activity.t(), User.t() | nil) :: boolean() - def visible_for_user?(%{actor: ap_id}, %User{ap_id: ap_id}), do: true + @spec visible_for_user?(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?(nil, _), do: false - def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false + def visible_for_user?(%Activity{data: %{"listMessage" => _}}, nil), do: false - def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{} = user) do + def visible_for_user?( + %Activity{data: %{"listMessage" => list_ap_id}} = activity, + %User{} = user + ) do user.ap_id in activity.data["to"] || list_ap_id |> Pleroma.List.get_by_ap_id() |> Pleroma.List.member?(user) end - def visible_for_user?(%{local: local} = activity, nil) do - cfg_key = if local, do: :local, else: :remote - - if Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key), + def visible_for_user?(%Activity{} = activity, nil) do + if restrict_unauthenticated_access?(activity), do: false, else: is_public?(activity) end - def visible_for_user?(activity, user) do + def visible_for_user?(%Activity{} = activity, user) 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)) @@ -82,6 +83,26 @@ def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do result end + def restrict_unauthenticated_access?(%Activity{local: local}) do + restrict_unauthenticated_access_to_activity?(local) + end + + def restrict_unauthenticated_access?(%Object{} = object) do + object + |> Object.local?() + |> restrict_unauthenticated_access_to_activity?() + end + + def restrict_unauthenticated_access?(%User{} = user) do + User.visible_for(user, _reading_user = nil) + end + + defp restrict_unauthenticated_access_to_activity?(local?) when is_boolean(local?) do + cfg_key = if local?, do: :local, else: :remote + + Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key) + end + def get_visibility(object) do to = object.data["to"] || [] cc = object.data["cc"] || [] 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 d5713c3dd..5c2c282b3 100644 --- a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex @@ -5,22 +5,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do use Pleroma.Web, :controller - import Pleroma.Web.ControllerHelper, only: [json_response: 3] + import Pleroma.Web.ControllerHelper, + only: [json_response: 3, fetch_integer_param: 3] alias Pleroma.Config alias Pleroma.MFA alias Pleroma.ModerationLog - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Stats alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.Pipeline alias Pleroma.Web.AdminAPI alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.AdminAPI.ModerationLogView - alias Pleroma.Web.AdminAPI.Search alias Pleroma.Web.Endpoint + alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Router @users_page_size 50 @@ -28,7 +26,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, %{scopes: ["read:accounts"], admin: true} - when action in [:list_users, :user_show, :right_get, :show_user_credentials] + when action in [:right_get, :show_user_credentials, :create_backup] ) plug( @@ -37,12 +35,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do when action in [ :get_password_reset, :force_password_reset, - :user_delete, - :users_create, - :user_toggle_activation, - :user_activate, - :user_deactivate, - :user_approve, :tag_users, :untag_users, :right_add, @@ -54,12 +46,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do ] ) - plug( - OAuthScopesPlug, - %{scopes: ["write:follows"], admin: true} - when action in [:user_follow, :user_unfollow] - ) - plug( OAuthScopesPlug, %{scopes: ["read:statuses"], admin: true} @@ -95,132 +81,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do action_fallback(AdminAPI.FallbackController) - def user_delete(conn, %{"nickname" => nickname}) do - user_delete(conn, %{"nicknames" => [nickname]}) - end - - def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do - users = - nicknames - |> Enum.map(&User.get_cached_by_nickname/1) - - users - |> Enum.each(fn user -> - {:ok, delete_data, _} = Builder.delete(admin, user.ap_id) - Pipeline.common_pipeline(delete_data, local: true) - end) - - ModerationLog.insert_log(%{ - actor: admin, - subject: users, - action: "delete" - }) - - json(conn, nicknames) - end - - def user_follow(%{assigns: %{user: admin}} = conn, %{ - "follower" => follower_nick, - "followed" => followed_nick - }) 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) - - ModerationLog.insert_log(%{ - actor: admin, - followed: followed, - follower: follower, - action: "follow" - }) - end - - json(conn, "ok") - end - - def user_unfollow(%{assigns: %{user: admin}} = conn, %{ - "follower" => follower_nick, - "followed" => followed_nick - }) 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) - - ModerationLog.insert_log(%{ - actor: admin, - followed: followed, - follower: follower, - action: "unfollow" - }) - end - - json(conn, "ok") - end - - def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do - changesets = - Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} -> - user_data = %{ - nickname: nickname, - name: nickname, - email: email, - password: password, - password_confirmation: password, - bio: "." - } - - User.register_changeset(%User{}, user_data, need_confirmation: false) - end) - |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi -> - Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset) - end) - - case Pleroma.Repo.transaction(changesets) do - {:ok, users} -> - res = - users - |> 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), - action: "create" - }) - - json(conn, res) - - {:error, id, changeset, _} -> - res = - Enum.map(changesets.operations, fn - {current_id, {:changeset, _current_changeset, _}} when current_id == id -> - AccountView.render("create-error.json", %{changeset: changeset}) - - {_, {:changeset, current_changeset, _}} -> - AccountView.render("create-error.json", %{changeset: current_changeset}) - end) - - conn - |> put_status(:conflict) - |> json(res) - end - end - - def user_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}) - else - _ -> {:error, :not_found} - end - end - 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) @@ -274,69 +134,6 @@ def list_user_chats(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} end end - def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do - user = User.get_cached_by_nickname(nickname) - - {:ok, updated_user} = User.deactivate(user, !user.deactivated) - - action = if user.deactivated, do: "activate", else: "deactivate" - - ModerationLog.insert_log(%{ - actor: admin, - subject: [user], - action: action - }) - - conn - |> put_view(AccountView) - |> render("show.json", %{user: updated_user}) - end - - def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do - users = Enum.map(nicknames, &User.get_cached_by_nickname/1) - {:ok, updated_users} = User.deactivate(users, false) - - ModerationLog.insert_log(%{ - actor: admin, - subject: users, - action: "activate" - }) - - conn - |> put_view(AccountView) - |> render("index.json", %{users: Keyword.values(updated_users)}) - end - - def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do - users = Enum.map(nicknames, &User.get_cached_by_nickname/1) - {:ok, updated_users} = User.deactivate(users, true) - - ModerationLog.insert_log(%{ - actor: admin, - subject: users, - action: "deactivate" - }) - - conn - |> put_view(AccountView) - |> render("index.json", %{users: Keyword.values(updated_users)}) - end - - def user_approve(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do - users = Enum.map(nicknames, &User.get_cached_by_nickname/1) - {:ok, updated_users} = User.approve(users) - - ModerationLog.insert_log(%{ - actor: admin, - subject: users, - action: "approve" - }) - - conn - |> put_view(AccountView) - |> render("index.json", %{users: updated_users}) - end - def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do with {:ok, _} <- User.tag(nicknames, tags) do ModerationLog.insert_log(%{ @@ -363,43 +160,6 @@ def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, " end end - def list_users(conn, params) do - {page, page_size} = page_params(params) - filters = maybe_parse_filters(params["filters"]) - - search_params = %{ - query: params["query"], - page: page, - page_size: page_size, - tags: params["tags"], - name: params["name"], - email: params["email"] - } - - with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do - json( - conn, - AccountView.render("index.json", - users: users, - count: count, - page_size: page_size - ) - ) - end - end - - @filters ~w(local external active deactivated need_approval is_admin is_moderator) - - @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{} - defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{} - - defp maybe_parse_filters(filters) do - filters - |> String.split(",") - |> Enum.filter(&Enum.member?(@filters, &1)) - |> Map.new(&{String.to_existing_atom(&1), true}) - end - def right_add_multiple(%{assigns: %{user: admin}} = conn, %{ "permission_group" => permission_group, "nicknames" => nicknames @@ -681,25 +441,19 @@ def stats(conn, params) do json(conn, %{"status_visibility" => counters}) end + def create_backup(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do + with %User{} = user <- User.get_by_nickname(nickname), + {:ok, _} <- Pleroma.User.Backup.create(user, admin.id) do + ModerationLog.insert_log(%{actor: admin, subject: user, action: "create_backup"}) + + json(conn, "") + end + end + defp page_params(params) do - {get_page(params["page"]), get_page_size(params["page_size"])} - end - - defp get_page(page_string) when is_nil(page_string), do: 1 - - defp get_page(page_string) do - case Integer.parse(page_string) do - {page, _} -> page - :error -> 1 - end - end - - defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size - - defp get_page_size(page_size_string) do - case Integer.parse(page_size_string) do - {page_size, _} -> page_size - :error -> @users_page_size - end + { + 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/controllers/chat_controller.ex b/lib/pleroma/web/admin_api/controllers/chat_controller.ex index 967600d69..af8ff8292 100644 --- a/lib/pleroma/web/admin_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/chat_controller.ex @@ -10,10 +10,10 @@ defmodule Pleroma.Web.AdminAPI.ChatController do alias Pleroma.Chat.MessageReference alias Pleroma.ModerationLog alias Pleroma.Pagination - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.AdminAPI alias Pleroma.Web.CommonAPI alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView + alias Pleroma.Web.Plugs.OAuthScopesPlug require Logger diff --git a/lib/pleroma/web/admin_api/controllers/config_controller.ex b/lib/pleroma/web/admin_api/controllers/config_controller.ex index 0df13007f..5d155af3d 100644 --- a/lib/pleroma/web/admin_api/controllers/config_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/config_controller.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do alias Pleroma.Config alias Pleroma.ConfigDB - alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action == :update) 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 504d9b517..37dbfeb72 100644 --- a/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex @@ -5,9 +5,9 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do use Pleroma.Web, :controller - alias Pleroma.Plugs.InstanceStatic - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.InstanceDocument + alias Pleroma.Web.Plugs.InstanceStatic + alias Pleroma.Web.Plugs.OAuthScopesPlug plug(Pleroma.Web.ApiSpec.CastAndValidate) diff --git a/lib/pleroma/web/admin_api/controllers/invite_controller.ex b/lib/pleroma/web/admin_api/controllers/invite_controller.ex index 7d169b8d2..6a9b4038a 100644 --- a/lib/pleroma/web/admin_api/controllers/invite_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/invite_controller.ex @@ -8,8 +8,8 @@ defmodule Pleroma.Web.AdminAPI.InviteController do import Pleroma.Web.ControllerHelper, only: [json_response: 3] alias Pleroma.Config - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.UserInviteToken + alias Pleroma.Web.Plugs.OAuthScopesPlug require Logger 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 131e22d78..6d92e9f7f 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 @@ -5,9 +5,9 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do use Pleroma.Web, :controller - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.ApiSpec.Admin, as: Spec alias Pleroma.Web.MediaProxy + alias Pleroma.Web.Plugs.OAuthScopesPlug plug(Pleroma.Web.ApiSpec.CastAndValidate) diff --git a/lib/pleroma/web/admin_api/controllers/oauth_app_controller.ex b/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex similarity index 97% rename from lib/pleroma/web/admin_api/controllers/oauth_app_controller.ex rename to lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex index dca23ea73..116a05a4d 100644 --- a/lib/pleroma/web/admin_api/controllers/oauth_app_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex @@ -7,8 +7,8 @@ defmodule Pleroma.Web.AdminAPI.OAuthAppController do import Pleroma.Web.ControllerHelper, only: [json_response: 3] - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.OAuth.App + alias Pleroma.Web.Plugs.OAuthScopesPlug require Logger diff --git a/lib/pleroma/web/admin_api/controllers/relay_controller.ex b/lib/pleroma/web/admin_api/controllers/relay_controller.ex index 95d06dde7..611388447 100644 --- a/lib/pleroma/web/admin_api/controllers/relay_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/relay_controller.ex @@ -6,8 +6,8 @@ defmodule Pleroma.Web.AdminAPI.RelayController do use Pleroma.Web, :controller alias Pleroma.ModerationLog - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.ActivityPub.Relay + alias Pleroma.Web.Plugs.OAuthScopesPlug require Logger @@ -33,11 +33,7 @@ def index(conn, _params) do def follow(%{assigns: %{user: admin}, body_params: %{relay_url: target}} = conn, _) do with {:ok, _message} <- Relay.follow(target) do - ModerationLog.insert_log(%{ - action: "relay_follow", - actor: admin, - target: target - }) + ModerationLog.insert_log(%{action: "relay_follow", actor: admin, target: target}) json(conn, %{actor: target, followed_back: target in Relay.following()}) else @@ -48,13 +44,9 @@ def follow(%{assigns: %{user: admin}, body_params: %{relay_url: target}} = conn, end end - def unfollow(%{assigns: %{user: admin}, body_params: %{relay_url: target}} = conn, _) do - with {:ok, _message} <- Relay.unfollow(target) do - ModerationLog.insert_log(%{ - action: "relay_unfollow", - actor: admin, - target: target - }) + def unfollow(%{assigns: %{user: admin}, body_params: %{relay_url: target} = params} = conn, _) do + with {:ok, _message} <- Relay.unfollow(target, %{force: params[:force]}) do + ModerationLog.insert_log(%{action: "relay_unfollow", actor: admin, target: target}) json(conn, target) else diff --git a/lib/pleroma/web/admin_api/controllers/report_controller.ex b/lib/pleroma/web/admin_api/controllers/report_controller.ex index 4c011e174..6a0e56f5f 100644 --- a/lib/pleroma/web/admin_api/controllers/report_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/report_controller.ex @@ -9,12 +9,12 @@ defmodule Pleroma.Web.AdminAPI.ReportController do alias Pleroma.Activity alias Pleroma.ModerationLog - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.ReportNote alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.AdminAPI alias Pleroma.Web.AdminAPI.Report alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Plugs.OAuthScopesPlug require Logger @@ -38,7 +38,7 @@ def index(conn, params) do end def show(conn, %{id: id}) do - with %Activity{} = report <- Activity.get_by_id(id) do + with %Activity{} = report <- Activity.get_report(id) do render(conn, "show.json", Report.extract_report_info(report)) else _ -> {:error, :not_found} diff --git a/lib/pleroma/web/admin_api/controllers/status_controller.ex b/lib/pleroma/web/admin_api/controllers/status_controller.ex index bc48cc527..2bb437cfe 100644 --- a/lib/pleroma/web/admin_api/controllers/status_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/status_controller.ex @@ -7,10 +7,10 @@ defmodule Pleroma.Web.AdminAPI.StatusController do alias Pleroma.Activity alias Pleroma.ModerationLog - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI + alias Pleroma.Web.Plugs.OAuthScopesPlug require Logger diff --git a/lib/pleroma/web/admin_api/controllers/user_controller.ex b/lib/pleroma/web/admin_api/controllers/user_controller.ex new file mode 100644 index 000000000..a2a1c875d --- /dev/null +++ b/lib/pleroma/web/admin_api/controllers/user_controller.ex @@ -0,0 +1,281 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.UserController do + use Pleroma.Web, :controller + + import Pleroma.Web.ControllerHelper, + only: [fetch_integer_param: 3] + + alias Pleroma.ModerationLog + alias Pleroma.User + 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( + OAuthScopesPlug, + %{scopes: ["read:accounts"], admin: true} + when action in [:list, :show] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"], admin: true} + when action in [ + :delete, + :create, + :toggle_activation, + :activate, + :deactivate, + :approve + ] + ) + + plug( + OAuthScopesPlug, + %{scopes: ["write:follows"], admin: true} + when action in [:follow, :unfollow] + ) + + action_fallback(AdminAPI.FallbackController) + + def delete(conn, %{"nickname" => nickname}) do + delete(conn, %{"nicknames" => [nickname]}) + end + + def delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do + users = Enum.map(nicknames, &User.get_cached_by_nickname/1) + + Enum.each(users, fn user -> + {:ok, delete_data, _} = Builder.delete(admin, user.ap_id) + Pipeline.common_pipeline(delete_data, local: true) + end) + + ModerationLog.insert_log(%{ + actor: admin, + subject: users, + action: "delete" + }) + + json(conn, nicknames) + end + + def follow(%{assigns: %{user: admin}} = conn, %{ + "follower" => follower_nick, + "followed" => followed_nick + }) 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) + + ModerationLog.insert_log(%{ + actor: admin, + followed: followed, + follower: follower, + action: "follow" + }) + end + + json(conn, "ok") + end + + def unfollow(%{assigns: %{user: admin}} = conn, %{ + "follower" => follower_nick, + "followed" => followed_nick + }) 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) + + ModerationLog.insert_log(%{ + actor: admin, + followed: followed, + follower: follower, + action: "unfollow" + }) + end + + json(conn, "ok") + end + + def create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do + changesets = + Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} -> + user_data = %{ + nickname: nickname, + name: nickname, + email: email, + password: password, + password_confirmation: password, + bio: "." + } + + User.register_changeset(%User{}, user_data, need_confirmation: false) + end) + |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi -> + Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset) + end) + + case Pleroma.Repo.transaction(changesets) do + {:ok, users} -> + res = + users + |> 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), + action: "create" + }) + + json(conn, res) + + {:error, id, changeset, _} -> + res = + Enum.map(changesets.operations, fn + {current_id, {:changeset, _current_changeset, _}} when current_id == id -> + AccountView.render("create-error.json", %{changeset: changeset}) + + {_, {:changeset, current_changeset, _}} -> + AccountView.render("create-error.json", %{changeset: current_changeset}) + end) + + conn + |> put_status(:conflict) + |> json(res) + end + end + + 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}) + else + _ -> {:error, :not_found} + end + end + + 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) + + action = if user.deactivated, do: "activate", else: "deactivate" + + ModerationLog.insert_log(%{ + actor: admin, + subject: [user], + action: action + }) + + conn + |> put_view(AccountView) + |> render("show.json", %{user: updated_user}) + end + + def activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do + users = Enum.map(nicknames, &User.get_cached_by_nickname/1) + {:ok, updated_users} = User.deactivate(users, false) + + ModerationLog.insert_log(%{ + actor: admin, + subject: users, + action: "activate" + }) + + conn + |> put_view(AccountView) + |> render("index.json", %{users: Keyword.values(updated_users)}) + end + + def deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do + users = Enum.map(nicknames, &User.get_cached_by_nickname/1) + {:ok, updated_users} = User.deactivate(users, true) + + ModerationLog.insert_log(%{ + actor: admin, + subject: users, + action: "deactivate" + }) + + conn + |> put_view(AccountView) + |> render("index.json", %{users: Keyword.values(updated_users)}) + end + + def approve(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do + users = Enum.map(nicknames, &User.get_cached_by_nickname/1) + {:ok, updated_users} = User.approve(users) + + ModerationLog.insert_log(%{ + actor: admin, + subject: users, + action: "approve" + }) + + conn + |> put_view(AccountView) + |> render("index.json", %{users: updated_users}) + end + + def list(conn, params) do + {page, page_size} = page_params(params) + filters = maybe_parse_filters(params["filters"]) + + search_params = + %{ + query: params["query"], + page: page, + page_size: page_size, + 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 + ) + ) + end + end + + @filters ~w(local external active deactivated need_approval unconfirmed is_admin is_moderator) + + @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{} + defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{} + + defp maybe_parse_filters(filters) do + filters + |> String.split(",") + |> Enum.filter(&Enum.member?(@filters, &1)) + |> Map.new(&{String.to_existing_atom(&1), true}) + end + + defp page_params(params) do + { + 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/views/account_view.ex b/lib/pleroma/web/admin_api/views/account_view.ex index 9c477feab..8bac24d3e 100644 --- a/lib/pleroma/web/admin_api/views/account_view.ex +++ b/lib/pleroma/web/admin_api/views/account_view.ex @@ -39,7 +39,7 @@ def render("credentials.json", %{user: user, for: for_user}) do :fields, :name, :nickname, - :locked, + :is_locked, :no_rich_text, :default_scope, :hide_follows, @@ -52,7 +52,7 @@ def render("credentials.json", %{user: user, for: for_user}) do :skip_thread_containment, :pleroma_settings_store, :raw_fields, - :discoverable, + :is_discoverable, :actor_type ]) |> Map.merge(%{ diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex index 773f798fe..535556370 100644 --- a/lib/pleroma/web/admin_api/views/report_view.ex +++ b/lib/pleroma/web/admin_api/views/report_view.ex @@ -52,7 +52,7 @@ def render("show.json", %{report: report, user: user, account: account, statuses end def render("index_notes.json", %{notes: notes}) when is_list(notes) do - Enum.map(notes, &render(__MODULE__, "show_note.json", &1)) + Enum.map(notes, &render(__MODULE__, "show_note.json", Map.from_struct(&1))) end def render("index_notes.json", _), do: [] diff --git a/lib/pleroma/web/api_spec/cast_and_validate.ex b/lib/pleroma/web/api_spec/cast_and_validate.ex index fbfc27d6f..6d1a7ebbc 100644 --- a/lib/pleroma/web/api_spec/cast_and_validate.ex +++ b/lib/pleroma/web/api_spec/cast_and_validate.ex @@ -115,6 +115,10 @@ defp cast_and_validate(spec, operation, conn, content_type, false = _strict) do %{reason: :unexpected_field, name: name, path: [name]}, params -> Map.delete(params, name) + # Filter out empty params + %{reason: :invalid_type, path: [name_atom], value: ""}, params -> + Map.delete(params, to_string(name_atom)) + %{reason: :invalid_enum, name: nil, path: path, value: value}, params -> path = path |> Enum.reverse() |> tl() |> Enum.reverse() |> list_items_to_string() update_in(params, path, &List.delete(&1, value)) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index a0933ba52..451aa2477 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -341,6 +341,7 @@ def mutes_operation do operationId: "AccountController.mutes", description: "Accounts the user has muted.", security: [%{"oAuth" => ["follow", "read:mutes"]}], + parameters: pagination_params(), responses: %{ 200 => Operation.response("Accounts", "application/json", array_of_accounts()) } @@ -354,6 +355,7 @@ def blocks_operation do operationId: "AccountController.blocks", description: "View your blocks. See also accounts/:id/{block,unblock}", security: [%{"oAuth" => ["read:blocks"]}], + parameters: pagination_params(), responses: %{ 200 => Operation.response("Accounts", "application/json", array_of_accounts()) } diff --git a/lib/pleroma/web/api_spec/operations/admin/oauth_app_operation.ex b/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex similarity index 100% rename from lib/pleroma/web/api_spec/operations/admin/oauth_app_operation.ex rename to lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex 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 e06b2d164..f754bb9f5 100644 --- a/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex @@ -56,7 +56,7 @@ def unfollow_operation do operationId: "AdminAPI.RelayController.unfollow", security: [%{"oAuth" => ["write:follows"]}], parameters: admin_api_params(), - requestBody: request_body("Parameters", relay_url()), + requestBody: request_body("Parameters", relay_unfollow()), responses: %{ 200 => Operation.response("Status", "application/json", %Schema{ @@ -91,4 +91,14 @@ defp relay_url do } } end + + defp relay_unfollow do + %Schema{ + type: :object, + properties: %{ + relay_url: %Schema{type: :string, format: :uri}, + force: %Schema{type: :boolean, default: false} + } + } + end end diff --git a/lib/pleroma/web/api_spec/operations/chat_operation.ex b/lib/pleroma/web/api_spec/operations/chat_operation.ex index 56554d5b4..560b81f17 100644 --- a/lib/pleroma/web/api_spec/operations/chat_operation.ex +++ b/lib/pleroma/web/api_spec/operations/chat_operation.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.BooleanLike alias Pleroma.Web.ApiSpec.Schemas.Chat alias Pleroma.Web.ApiSpec.Schemas.ChatMessage @@ -132,7 +133,10 @@ def index_operation do tags: ["chat"], summary: "Get a list of chats that you participated in", operationId: "ChatController.index", - parameters: pagination_params(), + parameters: [ + Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users") + | pagination_params() + ], responses: %{ 200 => Operation.response("The chats of the user", "application/json", chats_response()) }, @@ -158,7 +162,8 @@ def messages_operation do "The messages in the chat", "application/json", chat_messages_response() - ) + ), + 404 => Operation.response("Not Found", "application/json", ApiError) }, security: [ %{ diff --git a/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex new file mode 100644 index 000000000..6993794db --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex @@ -0,0 +1,79 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.ApiError + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %Operation{ + tags: ["Backups"], + summary: "List backups", + security: [%{"oAuth" => ["read:account"]}], + operationId: "PleromaAPI.BackupController.index", + responses: %{ + 200 => + Operation.response( + "An array of backups", + "application/json", + %Schema{ + type: :array, + items: backup() + } + ), + 400 => Operation.response("Bad Request", "application/json", ApiError) + } + } + end + + def create_operation do + %Operation{ + tags: ["Backups"], + summary: "Create a backup", + security: [%{"oAuth" => ["read:account"]}], + operationId: "PleromaAPI.BackupController.create", + responses: %{ + 200 => + Operation.response( + "An array of backups", + "application/json", + %Schema{ + type: :array, + items: backup() + } + ), + 400 => Operation.response("Bad Request", "application/json", ApiError) + } + } + end + + defp backup do + %Schema{ + title: "Backup", + description: "Response schema for a backup", + type: :object, + properties: %{ + inserted_at: %Schema{type: :string, format: :"date-time"}, + content_type: %Schema{type: :string}, + file_name: %Schema{type: :string}, + file_size: %Schema{type: :integer}, + processed: %Schema{type: :boolean} + }, + example: %{ + "content_type" => "application/zip", + "file_name" => + "https://cofe.fe:4000/media/backups/archive-foobar-20200908T164207-Yr7vuT5Wycv-sN3kSN2iJ0k-9pMo60j9qmvRCdDqIew.zip", + "file_size" => 4105, + "inserted_at" => "2020-09-08T16:42:07.000Z", + "processed" => true + } + } + end +end 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 efbfce75f..a56641426 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 @@ -126,7 +126,7 @@ def delete_operation do end defp name_param do - Operation.parameter(:name, :path, :string, "Pack Name", example: "cofe", required: true) + Operation.parameter(:name, :query, :string, "Pack Name", example: "cofe", required: true) end defp files_object do 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 59548af13..79f52dcb3 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 @@ -19,7 +19,21 @@ def remote_operation do tags: ["Emoji Packs"], summary: "Make request to another instance for emoji packs list", security: [%{"oAuth" => ["write"]}], - parameters: [url_param()], + parameters: [ + url_param(), + Operation.parameter( + :page, + :query, + %Schema{type: :integer, default: 1}, + "Page" + ), + Operation.parameter( + :page_size, + :query, + %Schema{type: :integer, default: 30}, + "Number of emoji to return" + ) + ], operationId: "PleromaAPI.EmojiPackController.remote", responses: %{ 200 => emoji_packs_response(), @@ -192,7 +206,7 @@ def import_from_filesystem_operation do end defp name_param do - Operation.parameter(:name, :path, :string, "Pack Name", example: "cofe", required: true) + Operation.parameter(:name, :query, :string, "Pack Name", example: "cofe", required: true) end defp url_param do diff --git a/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex new file mode 100644 index 000000000..2c455b0df --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex @@ -0,0 +1,40 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.PleromaInstancesOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def show_operation do + %Operation{ + tags: ["PleromaInstances"], + summary: "Instances federation status", + description: "Information about instances deemed unreachable by the server", + operationId: "PleromaInstances.show", + responses: %{ + 200 => Operation.response("PleromaInstances", "application/json", pleroma_instances()) + } + } + end + + def pleroma_instances do + %Schema{ + type: :object, + properties: %{ + unreachable: %Schema{ + type: :object, + properties: %{hostname: %Schema{type: :string, format: :"date-time"}} + } + }, + example: %{ + "unreachable" => %{"consistently-unreachable.name" => "2020-10-14 22:07:58.216473"} + } + } + end +end diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex index 8e19bace7..95720df9f 100644 --- a/lib/pleroma/web/api_spec/operations/timeline_operation.ex +++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex @@ -59,6 +59,7 @@ def public_operation do security: [%{"oAuth" => ["read:statuses"]}], parameters: [ local_param(), + instance_param(), only_media_param(), with_muted_param(), exclude_visibilities_param(), @@ -158,8 +159,17 @@ defp local_param do ) end + defp instance_param do + Operation.parameter( + :instance, + :query, + %Schema{type: :string}, + "Show only statuses from the given domain" + ) + end + defp with_muted_param do - Operation.parameter(:with_muted, :query, BooleanLike, "Includeactivities by muted users") + Operation.parameter(:with_muted, :query, BooleanLike, "Include activities by muted users") end defp exclude_visibilities_param do diff --git a/lib/pleroma/web/api_spec/schemas/chat.ex b/lib/pleroma/web/api_spec/schemas/chat.ex index b4986b734..65f908e33 100644 --- a/lib/pleroma/web/api_spec/schemas/chat.ex +++ b/lib/pleroma/web/api_spec/schemas/chat.ex @@ -50,7 +50,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Chat do "fields" => [] }, "statuses_count" => 1, - "locked" => false, + "is_locked" => false, "created_at" => "2020-04-16T13:40:15.000Z", "display_name" => "lain", "fields" => [], diff --git a/lib/pleroma/web/api_spec/schemas/poll.ex b/lib/pleroma/web/api_spec/schemas/poll.ex index c62096db0..0dfa60b97 100644 --- a/lib/pleroma/web/api_spec/schemas/poll.ex +++ b/lib/pleroma/web/api_spec/schemas/poll.ex @@ -28,8 +28,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do }, votes_count: %Schema{ type: :integer, - nullable: true, - description: "How many votes have been received. Number, or null if `multiple` is false." + description: "How many votes have been received. Number." + }, + voters_count: %Schema{ + type: :integer, + description: "How many unique accounts have voted. Number." }, voted: %Schema{ type: :boolean, @@ -61,7 +64,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do expired: true, multiple: false, votes_count: 10, - voters_count: nil, + voters_count: 10, voted: true, own_votes: [ 1 diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 947e42890..e6890df2d 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -252,7 +252,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "header" => "http://localhost:4001/images/banner.png", "header_static" => "http://localhost:4001/images/banner.png", "id" => "9toJCsKN7SmSf3aj5c", - "locked" => false, + "is_locked" => false, "note" => "Tester Number 6", "pleroma" => %{ "background_image" => nil, diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex index c611b3e09..d6d2a8d06 100644 --- a/lib/pleroma/web/auth/pleroma_authenticator.ex +++ b/lib/pleroma/web/auth/pleroma_authenticator.ex @@ -3,10 +3,10 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Auth.PleromaAuthenticator do - alias Pleroma.Plugs.AuthenticationPlug alias Pleroma.Registration alias Pleroma.Repo alias Pleroma.User + alias Pleroma.Web.Plugs.AuthenticationPlug import Pleroma.Web.Auth.Authenticator, only: [fetch_credentials: 1, fetch_user: 1] diff --git a/lib/pleroma/web/auth/totp_authenticator.ex b/lib/pleroma/web/auth/totp_authenticator.ex index 1794e407c..edc9871ea 100644 --- a/lib/pleroma/web/auth/totp_authenticator.ex +++ b/lib/pleroma/web/auth/totp_authenticator.ex @@ -5,8 +5,8 @@ defmodule Pleroma.Web.Auth.TOTPAuthenticator do alias Pleroma.MFA alias Pleroma.MFA.TOTP - alias Pleroma.Plugs.AuthenticationPlug alias Pleroma.User + alias Pleroma.Web.Plugs.AuthenticationPlug @doc "Verify code or check backup code." @spec verify(String.t(), User.t()) :: diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api.ex similarity index 99% rename from lib/pleroma/web/common_api/common_api.ex rename to lib/pleroma/web/common_api.ex index aa4c6ddab..0ab1b115d 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api.ex @@ -45,7 +45,8 @@ def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) {_, {:ok, %Activity{} = activity, _meta}} <- {:common_pipeline, Pipeline.common_pipeline(create_activity_data, - local: true + local: true, + idempotency_key: opts[:idempotency_key] )} do {:ok, activity} else diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 9d7b24eb2..3b71adf0e 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -12,12 +12,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do alias Pleroma.Conversation.Participation alias Pleroma.Formatter alias Pleroma.Object - alias Pleroma.Plugs.AuthenticationPlug alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.MediaProxy + alias Pleroma.Web.Plugs.AuthenticationPlug require Logger require Pleroma.Constants @@ -274,7 +274,7 @@ defp build_attachment_link(_), do: "" def format_input(text, format, options \\ []) @doc """ - Formatting text to plain text. + Formatting text to plain text, BBCode, HTML, or Markdown """ def format_input(text, "text/plain", options) do text @@ -285,9 +285,6 @@ def format_input(text, "text/plain", options) do end).() end - @doc """ - Formatting text as BBCode. - """ def format_input(text, "text/bbcode", options) do text |> String.replace(~r/\r/, "") @@ -297,18 +294,12 @@ def format_input(text, "text/bbcode", options) do |> Formatter.linkify(options) end - @doc """ - Formatting text to html. - """ def format_input(text, "text/html", options) do text |> Formatter.html_escape("text/html") |> Formatter.linkify(options) end - @doc """ - Formatting text to markdown. - """ def format_input(text, "text/markdown", options) do text |> Formatter.mentions_escape(options) diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index 6445966e0..69188a882 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -48,13 +48,13 @@ defp param_to_integer(val, default) when is_binary(val) do defp param_to_integer(_, default), do: default - def add_link_headers(conn, activities, extra_params \\ %{}) + def add_link_headers(conn, entries, extra_params \\ %{}) - def add_link_headers(%{assigns: %{skip_link_headers: true}} = conn, _activities, _extra_params), + def add_link_headers(%{assigns: %{skip_link_headers: true}} = conn, _entries, _extra_params), do: conn - def add_link_headers(conn, activities, extra_params) do - case get_pagination_fields(conn, activities, extra_params) do + def add_link_headers(conn, entries, extra_params) do + case get_pagination_fields(conn, entries, extra_params) do %{"next" => next_url, "prev" => prev_url} -> put_resp_header(conn, "link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"") @@ -78,19 +78,15 @@ defp build_pagination_fields(conn, min_id, max_id, extra_params) do } end - def get_pagination_fields(conn, activities, extra_params \\ %{}) do - case List.last(activities) do + def get_pagination_fields(conn, entries, extra_params \\ %{}) do + case List.last(entries) do %{pagination_id: max_id} when not is_nil(max_id) -> - %{pagination_id: min_id} = - activities - |> List.first() + %{pagination_id: min_id} = List.first(entries) build_pagination_fields(conn, min_id, max_id, extra_params) %{id: max_id} -> - %{id: min_id} = - activities - |> List.first() + %{id: min_id} = List.first(entries) build_pagination_fields(conn, min_id, max_id, extra_params) diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index 8b153763d..f26542e88 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -7,19 +7,23 @@ defmodule Pleroma.Web.Endpoint do require Pleroma.Constants + alias Pleroma.Config + socket("/socket", Pleroma.Web.UserSocket) - plug(Pleroma.Plugs.SetLocalePlug) + plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint]) + + plug(Pleroma.Web.Plugs.SetLocalePlug) plug(CORSPlug) - plug(Pleroma.Plugs.HTTPSecurityPlug) - plug(Pleroma.Plugs.UploadedMedia) + plug(Pleroma.Web.Plugs.HTTPSecurityPlug) + plug(Pleroma.Web.Plugs.UploadedMedia) @static_cache_control "public, no-cache" # 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.Plugs.InstanceStatic, + plug(Pleroma.Web.Plugs.InstanceStatic, at: "/", gzip: true, cache_control_for_etags: @static_cache_control, @@ -29,7 +33,7 @@ defmodule Pleroma.Web.Endpoint do ) # Careful! No `only` restriction here, as we don't know what frontends contain. - plug(Pleroma.Plugs.FrontendStatic, + plug(Pleroma.Web.Plugs.FrontendStatic, at: "/", frontend_type: :primary, gzip: true, @@ -41,7 +45,7 @@ defmodule Pleroma.Web.Endpoint do plug(Plug.Static.IndexHtml, at: "/pleroma/admin/") - plug(Pleroma.Plugs.FrontendStatic, + plug(Pleroma.Web.Plugs.FrontendStatic, at: "/pleroma/admin", frontend_type: :admin, gzip: true, @@ -79,26 +83,26 @@ defmodule Pleroma.Web.Endpoint do plug(Phoenix.CodeReloader) end - plug(Pleroma.Plugs.TrailingFormatPlug) + plug(Pleroma.Web.Plugs.TrailingFormatPlug) plug(Plug.RequestId) plug(Plug.Logger, log: :debug) plug(Plug.Parsers, parsers: [ :urlencoded, - {:multipart, length: {Pleroma.Config, :get, [[:instance, :upload_limit]]}}, + {:multipart, length: {Config, :get, [[:instance, :upload_limit]]}}, :json ], pass: ["*/*"], json_decoder: Jason, - length: Pleroma.Config.get([:instance, :upload_limit]), + length: Config.get([:instance, :upload_limit]), body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []} ) plug(Plug.MethodOverride) plug(Plug.Head) - secure_cookies = Pleroma.Config.get([__MODULE__, :secure_cookie_flag]) + secure_cookies = Config.get([__MODULE__, :secure_cookie_flag]) cookie_name = if secure_cookies, @@ -106,7 +110,7 @@ defmodule Pleroma.Web.Endpoint do else: "pleroma_key" extra = - Pleroma.Config.get([__MODULE__, :extra_cookie_attrs]) + Config.get([__MODULE__, :extra_cookie_attrs]) |> Enum.join(";") # The session will be stored in the cookie and signed, @@ -116,13 +120,13 @@ defmodule Pleroma.Web.Endpoint do Plug.Session, store: :cookie, key: cookie_name, - signing_salt: Pleroma.Config.get([__MODULE__, :signing_salt], "CqaoopA2"), + signing_salt: Config.get([__MODULE__, :signing_salt], "CqaoopA2"), http_only: true, secure: secure_cookies, extra: extra ) - plug(Pleroma.Plugs.RemoteIp) + plug(Pleroma.Web.Plugs.RemoteIp) defmodule Instrumenter do use Prometheus.PhoenixInstrumenter @@ -136,8 +140,34 @@ defmodule MetricsExporter do use Prometheus.PlugExporter end + defmodule MetricsExporterCaller do + @behaviour Plug + + def init(opts), do: opts + + def call(conn, opts) do + prometheus_config = Application.get_env(:prometheus, MetricsExporter, []) + ip_whitelist = List.wrap(prometheus_config[:ip_whitelist]) + + cond do + !prometheus_config[:enabled] -> + conn + + ip_whitelist != [] and + !Enum.find(ip_whitelist, fn ip -> + Pleroma.Helpers.InetHelper.parse_address(ip) == {:ok, conn.remote_ip} + end) -> + conn + + true -> + MetricsExporter.call(conn, opts) + end + end + end + plug(PipelineInstrumenter) - plug(MetricsExporter) + + plug(MetricsExporterCaller) plug(Pleroma.Web.Router) diff --git a/lib/pleroma/web/fallback_redirect_controller.ex b/lib/pleroma/web/fallback/redirect_controller.ex similarity index 95% rename from lib/pleroma/web/fallback_redirect_controller.ex rename to lib/pleroma/web/fallback/redirect_controller.ex index 431ad5485..6f759d559 100644 --- a/lib/pleroma/web/fallback_redirect_controller.ex +++ b/lib/pleroma/web/fallback/redirect_controller.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Fallback.RedirectController do +defmodule Pleroma.Web.Fallback.RedirectController do use Pleroma.Web, :controller require Logger @@ -75,7 +75,7 @@ def empty(conn, _params) do end defp index_file_path do - Pleroma.Plugs.InstanceStatic.file_path("index.html") + Pleroma.Web.Plugs.InstanceStatic.file_path("index.html") end defp build_tags(conn, params) do diff --git a/lib/pleroma/web/fed_sockets/fed_sockets.ex b/lib/pleroma/web/fed_sockets.ex similarity index 100% rename from lib/pleroma/web/fed_sockets/fed_sockets.ex rename to lib/pleroma/web/fed_sockets.ex diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator.ex similarity index 100% rename from lib/pleroma/web/federator/federator.ex rename to lib/pleroma/web/federator.ex diff --git a/lib/pleroma/web/feed/tag_controller.ex b/lib/pleroma/web/feed/tag_controller.ex index 93a8294b7..218cdbdf3 100644 --- a/lib/pleroma/web/feed/tag_controller.ex +++ b/lib/pleroma/web/feed/tag_controller.ex @@ -10,14 +10,14 @@ defmodule Pleroma.Web.Feed.TagController do alias Pleroma.Web.Feed.FeedView def feed(conn, params) do - unless Pleroma.Config.restrict_unauthenticated_access?(:activities, :local) do + if Config.get!([:instance, :public]) do render_feed(conn, params) else render_error(conn, :not_found, "Not found") end end - def render_feed(conn, %{"tag" => raw_tag} = params) do + defp render_feed(conn, %{"tag" => raw_tag} = params) do {format, tag} = parse_tag(raw_tag) activities = @@ -36,12 +36,13 @@ def render_feed(conn, %{"tag" => raw_tag} = params) do end @spec parse_tag(binary() | any()) :: {format :: String.t(), tag :: String.t()} - defp parse_tag(raw_tag) when is_binary(raw_tag) do - case Enum.reverse(String.split(raw_tag, ".")) do - [format | tag] when format in ["atom", "rss"] -> {format, Enum.join(tag, ".")} - _ -> {"rss", raw_tag} + defp parse_tag(raw_tag) do + case is_binary(raw_tag) && Enum.reverse(String.split(raw_tag, ".")) do + [format | tag] when format in ["rss", "atom"] -> + {format, Enum.join(tag, ".")} + + _ -> + {"atom", raw_tag} end end - - defp parse_tag(raw_tag), do: {"rss", raw_tag} end diff --git a/lib/pleroma/web/feed/user_controller.ex b/lib/pleroma/web/feed/user_controller.ex index 71eb1ea7e..a5013d2c0 100644 --- a/lib/pleroma/web/feed/user_controller.ex +++ b/lib/pleroma/web/feed/user_controller.ex @@ -5,30 +5,25 @@ defmodule Pleroma.Web.Feed.UserController do use Pleroma.Web, :controller - alias Fallback.RedirectController + alias Pleroma.Config alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPubController alias Pleroma.Web.Feed.FeedView - plug(Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect]) + plug(Pleroma.Web.Plugs.SetFormatPlug when action in [:feed_redirect]) action_fallback(:errors) def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do - RedirectController.redirector_with_meta(conn, %{user: user}) + Pleroma.Web.Fallback.RedirectController.redirector_with_meta(conn, %{user: user}) end end def feed_redirect(%{assigns: %{format: format}} = conn, _params) when format in ["json", "activity+json"] do - with %{halted: false} = conn <- - Pleroma.Plugs.EnsureAuthenticatedPlug.call(conn, - unless_func: &Pleroma.Web.FederatingPlug.federating?/1 - ) do - ActivityPubController.call(conn, :user) - end + ActivityPubController.call(conn, :user) end def feed_redirect(conn, %{"nickname" => nickname}) do @@ -37,25 +32,18 @@ def feed_redirect(conn, %{"nickname" => nickname}) do end end - def feed(conn, params) do - unless Pleroma.Config.restrict_unauthenticated_access?(:profiles, :local) do - render_feed(conn, params) - else - errors(conn, {:error, :not_found}) - end - end - - def render_feed(conn, %{"nickname" => nickname} = params) do + def feed(conn, %{"nickname" => nickname} = params) do format = get_format(conn) format = - if format in ["rss", "atom"] do + if format in ["atom", "rss"] do format else "atom" end - with {_, %User{local: true} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do + with {_, %User{local: true} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)}, + {_, :visible} <- {:visibility, User.visible_for(user, _reading_user = nil)} do activities = %{ type: ["Create"], @@ -70,7 +58,7 @@ def render_feed(conn, %{"nickname" => nickname} = params) do |> render("user.#{format}", user: user, activities: activities, - feed_config: Pleroma.Config.get([:feed]) + feed_config: Config.get([:feed]) ) end end @@ -82,6 +70,8 @@ def errors(conn, {:error, :not_found}) do def errors(conn, {:fetch_user, %User{local: false}}), do: errors(conn, {:error, :not_found}) def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found}) + def errors(conn, {:visibility, _}), do: errors(conn, {:error, :not_found}) + def errors(conn, _) do render_error(conn, :internal_server_error, "Something went wrong") end diff --git a/lib/pleroma/web/mailer/subscription_controller.ex b/lib/pleroma/web/mailer/subscription_controller.ex index 478a83518..ace44afd1 100644 --- a/lib/pleroma/web/mailer/subscription_controller.ex +++ b/lib/pleroma/web/mailer/subscription_controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Mailer.SubscriptionController do use Pleroma.Web, :controller diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex index 43ec70021..08f92d55f 100644 --- a/lib/pleroma/web/masto_fe_controller.ex +++ b/lib/pleroma/web/masto_fe_controller.ex @@ -5,9 +5,9 @@ defmodule Pleroma.Web.MastoFEController do use Pleroma.Web, :controller - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User + alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :put_settings) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index ca1a79f5e..784fdc975 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -15,9 +15,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do ] alias Pleroma.Maps - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.RateLimiter alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Builder @@ -29,6 +26,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.OAuth.OAuthController alias Pleroma.Web.OAuth.OAuthView + alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.RateLimiter alias Pleroma.Web.TwitterAPI.TwitterAPI plug(Pleroma.Web.ApiSpec.CastAndValidate) @@ -177,7 +177,6 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p user_params = [ :no_rich_text, - :locked, :hide_followers_count, :hide_follows_count, :hide_followers, @@ -186,7 +185,6 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p :show_role, :skip_thread_containment, :allow_following_move, - :discoverable, :accepts_chat_messages ] |> Enum.reduce(%{}, fn key, acc -> @@ -210,6 +208,8 @@ 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(:is_locked, params[:locked]) + |> Maps.put_if_present(:is_discoverable, params[:discoverable]) # What happens here: # @@ -442,15 +442,27 @@ def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do end @doc "GET /api/v1/mutes" - def mutes(%{assigns: %{user: user}} = conn, _) do - users = User.muted_users(user, _restrict_deactivated = true) - render(conn, "index.json", users: users, for: user, as: :user) + def mutes(%{assigns: %{user: user}} = conn, params) do + users = + user + |> User.muted_users_relation(_restrict_deactivated = true) + |> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true)) + + conn + |> add_link_headers(users) + |> render("index.json", users: users, for: user, as: :user) end @doc "GET /api/v1/blocks" - def blocks(%{assigns: %{user: user}} = conn, _) do - users = User.blocked_users(user, _restrict_deactivated = true) - render(conn, "index.json", users: users, for: user, as: :user) + def blocks(%{assigns: %{user: user}} = conn, params) do + users = + user + |> User.blocked_users_relation(_restrict_deactivated = true) + |> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true)) + + conn + |> add_link_headers(users) + |> render("index.json", users: users, for: user, as: :user) end @doc "GET /api/v1/endorsements" diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex index a516b6c20..143dcf80c 100644 --- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex @@ -5,12 +5,12 @@ defmodule Pleroma.Web.MastodonAPI.AppController do use Pleroma.Web, :controller - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token + alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug action_fallback(Pleroma.Web.MastodonAPI.FallbackController) diff --git a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex index 57c0be5fe..9cc3984d0 100644 --- a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex @@ -15,7 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do action_fallback(Pleroma.Web.MastodonAPI.FallbackController) - plug(Pleroma.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset) + plug(Pleroma.Web.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset) @local_mastodon_name "Mastodon-Local" @@ -24,7 +24,7 @@ def login(%{assigns: %{user: %User{}}} = conn, _params) do redirect(conn, to: local_mastodon_root_path(conn)) end - @doc "Local Mastodon FE login init action" + # Local Mastodon FE login init action def login(conn, %{"code" => auth_token}) do with {:ok, app} <- get_or_make_app(), {:ok, auth} <- Authorization.get_by_token(app, auth_token), @@ -35,7 +35,7 @@ def login(conn, %{"code" => auth_token}) do end end - @doc "Local Mastodon FE callback action" + # Local Mastodon FE callback action def login(conn, _) do with {:ok, app} <- get_or_make_app() do path = diff --git a/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex b/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex index f35ec3596..61347d8db 100644 --- a/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex @@ -8,8 +8,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationController do import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] alias Pleroma.Conversation.Participation - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo + alias Pleroma.Web.Plugs.OAuthScopesPlug action_fallback(Pleroma.Web.MastodonAPI.FallbackController) 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 c5f47c5df..872cb1f4d 100644 --- a/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiController do plug( :skip_plug, - [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug] + [Pleroma.Web.Plugs.OAuthScopesPlug, Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug] when action == :index ) 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 9c2d093cd..503bd7d5f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex @@ -5,8 +5,8 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do use Pleroma.Web, :controller - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User + alias Pleroma.Web.Plugs.OAuthScopesPlug plug(Pleroma.Web.ApiSpec.CastAndValidate) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DomainBlockOperation diff --git a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex index abbf0ce02..c71a34b15 100644 --- a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.FilterController do use Pleroma.Web, :controller alias Pleroma.Filter - alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug @oauth_read_actions [:show, :index] 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 748b6b475..f8cd7fa9f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex @@ -5,9 +5,9 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do use Pleroma.Web, :controller - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Plugs.OAuthScopesPlug plug(:put_view, Pleroma.Web.MastodonAPI.AccountView) plug(Pleroma.Web.ApiSpec.CastAndValidate) diff --git a/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex b/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex index d8859731d..07a32491a 100644 --- a/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceController do plug( :skip_plug, - [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug] + [Pleroma.Web.Plugs.OAuthScopesPlug, Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug] when action in [:show, :peers] ) diff --git a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex index 5daeaa780..f6b51bf02 100644 --- a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex @@ -5,9 +5,9 @@ defmodule Pleroma.Web.MastodonAPI.ListController do use Pleroma.Web, :controller - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.MastodonAPI.AccountView + alias Pleroma.Web.Plugs.OAuthScopesPlug @oauth_read_actions [:index, :show, :list_accounts] diff --git a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex index 85310edfa..0628b2b49 100644 --- a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex @@ -4,7 +4,7 @@ defmodule Pleroma.Web.MastodonAPI.MarkerController do use Pleroma.Web, :controller - alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug plug(Pleroma.Web.ApiSpec.CastAndValidate) 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 e7767de4e..9cf682c7b 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -17,7 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do plug( :skip_plug, - [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug] + [Pleroma.Web.Plugs.OAuthScopesPlug, Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug] when action in [:empty_array, :empty_object] ) diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex index 513de279f..161193134 100644 --- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex @@ -6,11 +6,12 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do use Pleroma.Web, :controller alias Pleroma.Object - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.Plugs.OAuthScopesPlug action_fallback(Pleroma.Web.MastodonAPI.FallbackController) + plug(Majic.Plug, [pool: Pleroma.MajicPool] when action in [:create, :create2]) plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(:put_view, Pleroma.Web.MastodonAPI.StatusView) diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex index e25cef30b..c3c8606f2 100644 --- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex @@ -8,8 +8,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] alias Pleroma.Notification - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.MastodonAPI.MastodonAPI + alias Pleroma.Web.Plugs.OAuthScopesPlug @oauth_read_actions [:show, :index] diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex index db46ffcfc..3dcd1c44f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex @@ -9,9 +9,9 @@ defmodule Pleroma.Web.MastodonAPI.PollController do alias Pleroma.Activity alias Pleroma.Object - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Plugs.OAuthScopesPlug action_fallback(Pleroma.Web.MastodonAPI.FallbackController) diff --git a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex index 405167108..156544f40 100644 --- a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex @@ -3,14 +3,12 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.ReportController do - alias Pleroma.Plugs.OAuthScopesPlug - use Pleroma.Web, :controller action_fallback(Pleroma.Web.MastodonAPI.FallbackController) plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(OAuthScopesPlug, %{scopes: ["write:reports"]} when action == :create) + plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["write:reports"]} when action == :create) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ReportOperation 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 1719c67ea..322a46497 100644 --- a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex @@ -7,9 +7,9 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.ScheduledActivity alias Pleroma.Web.MastodonAPI.MastodonAPI + alias Pleroma.Web.Plugs.OAuthScopesPlug @oauth_read_actions [:show, :index] diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex index 5a983db39..0043c3a56 100644 --- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex @@ -6,14 +6,14 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do use Pleroma.Web, :controller alias Pleroma.Activity - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.RateLimiter alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web alias Pleroma.Web.ControllerHelper alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.StatusView + alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.RateLimiter require Logger diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index da14c0b6c..4d9be5240 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -13,8 +13,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do alias Pleroma.Activity alias Pleroma.Bookmark alias Pleroma.Object - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.RateLimiter alias Pleroma.Repo alias Pleroma.ScheduledActivity alias Pleroma.User @@ -23,9 +21,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.ScheduledActivityView + alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.RateLimiter plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show]) + + plug( + :skip_plug, + Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show] + ) @unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []} @@ -123,9 +127,8 @@ def index(%{assigns: %{user: user}} = conn, %{ids: ids} = _params) do @doc """ POST /api/v1/statuses - - Creates a scheduled status when `scheduled_at` param is present and it's far enough """ + # Creates a scheduled status when `scheduled_at` param is present and it's far enough def create( %{ assigns: %{user: user}, @@ -156,11 +159,7 @@ def create( end end - @doc """ - POST /api/v1/statuses - - Creates a regular status - """ + # 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]) diff --git a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex index 34eac97c5..20138908c 100644 --- a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex @@ -13,7 +13,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(:restrict_push_enabled) - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]}) + plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["push"]}) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.SubscriptionOperation diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex index f91df9ab7..5765271cf 100644 --- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex @@ -8,7 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do require Logger plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action == :index) + plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action == :index) def open_api_operation(action) do operation = String.to_existing_atom("#{action}_operation") diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 5272790d3..ac96520a3 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -10,11 +10,11 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do alias Pleroma.Config alias Pleroma.Pagination - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.RateLimiter alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.RateLimiter plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:public, :hashtag]) @@ -111,6 +111,7 @@ def public(%{assigns: %{user: user}} = conn, params) do |> Map.put(:blocking_user, user) |> Map.put(:muting_user, user) |> Map.put(:reply_filtering_user, user) + |> Map.put(:instance, params[:instance]) |> ActivityPub.fetch_public_activities() conn diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 121ba1693..3158d09ed 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -242,7 +242,7 @@ defp do_render("show.json", %{user: user} = opts) do username: username_from_nickname(user.nickname), acct: user.nickname, display_name: display_name, - locked: user.locked, + locked: user.is_locked, created_at: Utils.to_masto_date(user.inserted_at), followers_count: followers_count, following_count: following_count, @@ -261,7 +261,7 @@ defp do_render("show.json", %{user: user} = opts) do sensitive: false, fields: user.raw_fields, pleroma: %{ - discoverable: user.discoverable, + discoverable: user.is_discoverable, actor_type: user.actor_type } }, @@ -388,7 +388,7 @@ defp maybe_put_unread_conversation_count(data, %User{id: user_id} = user, %User{ data |> Kernel.put_in( [:pleroma, :unread_conversation_count], - user.unread_conversation_count + Pleroma.Conversation.Participation.unread_count(user) ) end diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex index a91994915..82fcff062 100644 --- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex +++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex @@ -33,8 +33,15 @@ def render("participation.json", %{participation: participation, for: user}) do end activity = Activity.get_by_id_with_object(last_activity_id) - # Conversations return all users except the current user. - users = Enum.reject(participation.recipients, &(&1.id == user.id)) + + # Conversations return all users except the current user, + # except when the current user is the only participant + users = + if length(participation.recipients) > 1 do + Enum.reject(participation.recipients, &(&1.id == user.id)) + else + participation.recipients + end %{ id: participation.id |> to_string(), @@ -43,7 +50,8 @@ def render("participation.json", %{participation: participation, for: user}) do last_status: render(StatusView, "show.json", activity: activity, - direct_conversation_id: participation.id + direct_conversation_id: participation.id, + for: user ) } end diff --git a/lib/pleroma/web/mastodon_api/views/poll_view.ex b/lib/pleroma/web/mastodon_api/views/poll_view.ex index 1208dc9a0..4101f21d0 100644 --- a/lib/pleroma/web/mastodon_api/views/poll_view.ex +++ b/lib/pleroma/web/mastodon_api/views/poll_view.ex @@ -19,7 +19,7 @@ def render("show.json", %{object: object, multiple: multiple, options: options} expired: expired, multiple: multiple, votes_count: votes_count, - voters_count: (multiple || nil) && voters_count(object), + voters_count: voters_count(object), options: options, voted: voted?(params), emojis: Pleroma.Web.MastodonAPI.StatusView.build_emojis(object.data["emoji"]) diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy.ex similarity index 100% rename from lib/pleroma/web/media_proxy/media_proxy.ex rename to lib/pleroma/web/media_proxy.ex diff --git a/lib/pleroma/web/media_proxy/invalidations/http.ex b/lib/pleroma/web/media_proxy/invalidation/http.ex similarity index 97% rename from lib/pleroma/web/media_proxy/invalidations/http.ex rename to lib/pleroma/web/media_proxy/invalidation/http.ex index bb81d8888..0b0cde68c 100644 --- a/lib/pleroma/web/media_proxy/invalidations/http.ex +++ b/lib/pleroma/web/media_proxy/invalidation/http.ex @@ -30,7 +30,7 @@ defp do_purge(method, url, headers, options) do {:ok, %{status: status} = env} when 400 <= status and status < 500 -> {:error, env} - {:error, error} = error -> + {:error, _} = error -> error _ -> diff --git a/lib/pleroma/web/media_proxy/invalidations/script.ex b/lib/pleroma/web/media_proxy/invalidation/script.ex similarity index 100% rename from lib/pleroma/web/media_proxy/invalidations/script.ex rename to lib/pleroma/web/media_proxy/invalidation/script.ex diff --git a/lib/pleroma/web/metadata/feed.ex b/lib/pleroma/web/metadata/providers/feed.ex similarity index 100% rename from lib/pleroma/web/metadata/feed.ex rename to lib/pleroma/web/metadata/providers/feed.ex diff --git a/lib/pleroma/web/metadata/opengraph.ex b/lib/pleroma/web/metadata/providers/open_graph.ex similarity index 100% rename from lib/pleroma/web/metadata/opengraph.ex rename to lib/pleroma/web/metadata/providers/open_graph.ex diff --git a/lib/pleroma/web/metadata/provider.ex b/lib/pleroma/web/metadata/providers/provider.ex similarity index 100% rename from lib/pleroma/web/metadata/provider.ex rename to lib/pleroma/web/metadata/providers/provider.ex diff --git a/lib/pleroma/web/metadata/rel_me.ex b/lib/pleroma/web/metadata/providers/rel_me.ex similarity index 100% rename from lib/pleroma/web/metadata/rel_me.ex rename to lib/pleroma/web/metadata/providers/rel_me.ex diff --git a/lib/pleroma/web/metadata/restrict_indexing.ex b/lib/pleroma/web/metadata/providers/restrict_indexing.ex similarity index 87% rename from lib/pleroma/web/metadata/restrict_indexing.ex rename to lib/pleroma/web/metadata/providers/restrict_indexing.ex index a1dcb6e15..900c2434d 100644 --- a/lib/pleroma/web/metadata/restrict_indexing.ex +++ b/lib/pleroma/web/metadata/providers/restrict_indexing.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Web.Metadata.Providers.RestrictIndexing do """ @impl true - def build_tags(%{user: %{local: true, discoverable: true}}), do: [] + def build_tags(%{user: %{local: true, is_discoverable: true}}), do: [] def build_tags(_) do [ diff --git a/lib/pleroma/web/metadata/twitter_card.ex b/lib/pleroma/web/metadata/providers/twitter_card.ex similarity index 100% rename from lib/pleroma/web/metadata/twitter_card.ex rename to lib/pleroma/web/metadata/providers/twitter_card.ex diff --git a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex b/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex similarity index 93% rename from lib/pleroma/web/mongooseim/mongoose_im_controller.ex rename to lib/pleroma/web/mongoose_im/mongoose_im_controller.ex index 6cbbe8fd8..2a5c7c356 100644 --- a/lib/pleroma/web/mongooseim/mongoose_im_controller.ex +++ b/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex @@ -5,10 +5,10 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do use Pleroma.Web, :controller - alias Pleroma.Plugs.AuthenticationPlug - alias Pleroma.Plugs.RateLimiter alias Pleroma.Repo alias Pleroma.User + alias Pleroma.Web.Plugs.AuthenticationPlug + alias Pleroma.Web.Plugs.RateLimiter plug(RateLimiter, [name: :authentication] when action in [:user_exists, :check_password]) plug(RateLimiter, [name: :authentication, params: ["user"]] when action == :check_password) diff --git a/lib/pleroma/web/oauth.ex b/lib/pleroma/web/o_auth.ex similarity index 100% rename from lib/pleroma/web/oauth.ex rename to lib/pleroma/web/o_auth.ex diff --git a/lib/pleroma/web/oauth/app.ex b/lib/pleroma/web/o_auth/app.ex similarity index 100% rename from lib/pleroma/web/oauth/app.ex rename to lib/pleroma/web/o_auth/app.ex diff --git a/lib/pleroma/web/oauth/authorization.ex b/lib/pleroma/web/o_auth/authorization.ex similarity index 100% rename from lib/pleroma/web/oauth/authorization.ex rename to lib/pleroma/web/o_auth/authorization.ex diff --git a/lib/pleroma/web/oauth/fallback_controller.ex b/lib/pleroma/web/o_auth/fallback_controller.ex similarity index 100% rename from lib/pleroma/web/oauth/fallback_controller.ex rename to lib/pleroma/web/o_auth/fallback_controller.ex diff --git a/lib/pleroma/web/oauth/mfa_controller.ex b/lib/pleroma/web/o_auth/mfa_controller.ex similarity index 100% rename from lib/pleroma/web/oauth/mfa_controller.ex rename to lib/pleroma/web/o_auth/mfa_controller.ex diff --git a/lib/pleroma/web/oauth/mfa_view.ex b/lib/pleroma/web/o_auth/mfa_view.ex similarity index 100% rename from lib/pleroma/web/oauth/mfa_view.ex rename to lib/pleroma/web/o_auth/mfa_view.ex diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/o_auth/o_auth_controller.ex similarity index 99% rename from lib/pleroma/web/oauth/oauth_controller.ex rename to lib/pleroma/web/o_auth/o_auth_controller.ex index a4152e840..d2f9d1ceb 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/o_auth/o_auth_controller.ex @@ -8,7 +8,6 @@ defmodule Pleroma.Web.OAuth.OAuthController do alias Pleroma.Helpers.UriHelper alias Pleroma.Maps alias Pleroma.MFA - alias Pleroma.Plugs.RateLimiter alias Pleroma.Registration alias Pleroma.Repo alias Pleroma.User @@ -23,6 +22,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do alias Pleroma.Web.OAuth.Token alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken + alias Pleroma.Web.Plugs.RateLimiter require Logger @@ -31,7 +31,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do plug(:fetch_session) plug(:fetch_flash) - plug(:skip_plug, [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug]) + plug(:skip_plug, [ + Pleroma.Web.Plugs.OAuthScopesPlug, + Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug + ]) plug(RateLimiter, [name: :authentication] when action == :create_authorization) diff --git a/lib/pleroma/web/oauth/oauth_view.ex b/lib/pleroma/web/o_auth/o_auth_view.ex similarity index 100% rename from lib/pleroma/web/oauth/oauth_view.ex rename to lib/pleroma/web/o_auth/o_auth_view.ex diff --git a/lib/pleroma/web/oauth/scopes.ex b/lib/pleroma/web/o_auth/scopes.ex similarity index 97% rename from lib/pleroma/web/oauth/scopes.ex rename to lib/pleroma/web/o_auth/scopes.ex index 6f06f1431..90b9a0471 100644 --- a/lib/pleroma/web/oauth/scopes.ex +++ b/lib/pleroma/web/o_auth/scopes.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Web.OAuth.Scopes do Functions for dealing with scopes. """ - alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug @doc """ Fetch scopes from request params. diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/o_auth/token.ex similarity index 100% rename from lib/pleroma/web/oauth/token.ex rename to lib/pleroma/web/o_auth/token.ex diff --git a/lib/pleroma/web/oauth/token/query.ex b/lib/pleroma/web/o_auth/token/query.ex similarity index 100% rename from lib/pleroma/web/oauth/token/query.ex rename to lib/pleroma/web/o_auth/token/query.ex diff --git a/lib/pleroma/web/oauth/token/strategy/refresh_token.ex b/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex similarity index 100% rename from lib/pleroma/web/oauth/token/strategy/refresh_token.ex rename to lib/pleroma/web/o_auth/token/strategy/refresh_token.ex diff --git a/lib/pleroma/web/oauth/token/strategy/revoke.ex b/lib/pleroma/web/o_auth/token/strategy/revoke.ex similarity index 100% rename from lib/pleroma/web/oauth/token/strategy/revoke.ex rename to lib/pleroma/web/o_auth/token/strategy/revoke.ex diff --git a/lib/pleroma/web/oauth/token/utils.ex b/lib/pleroma/web/o_auth/token/utils.ex similarity index 100% rename from lib/pleroma/web/oauth/token/utils.ex rename to lib/pleroma/web/o_auth/token/utils.ex diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/o_status/o_status_controller.ex similarity index 89% rename from lib/pleroma/web/ostatus/ostatus_controller.ex rename to lib/pleroma/web/o_status/o_status_controller.ex index de1b0b3f0..668ae0ea4 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/o_status/o_status_controller.ex @@ -5,28 +5,24 @@ defmodule Pleroma.Web.OStatus.OStatusController do use Pleroma.Web, :controller - alias Fallback.RedirectController alias Pleroma.Activity alias Pleroma.Object - alias Pleroma.Plugs.RateLimiter alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPubController alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Endpoint + alias Pleroma.Web.Fallback.RedirectController alias Pleroma.Web.Metadata.PlayerView + alias Pleroma.Web.Plugs.RateLimiter alias Pleroma.Web.Router - plug(Pleroma.Plugs.EnsureAuthenticatedPlug, - unless_func: &Pleroma.Web.FederatingPlug.federating?/1 - ) - plug( RateLimiter, [name: :ap_routes, params: ["uuid"]] when action in [:object, :activity] ) plug( - Pleroma.Plugs.SetFormatPlug + Pleroma.Web.Plugs.SetFormatPlug when action in [:object, :activity, :notice] ) @@ -37,14 +33,12 @@ def object(%{assigns: %{format: format}} = conn, _params) ActivityPubController.call(conn, :object) end - def object(%{assigns: %{format: format}} = conn, _params) do + def object(conn, _params) do with id <- Endpoint.url() <> conn.request_path, {_, %Activity{} = activity} <- {:activity, Activity.get_create_by_object_ap_id_with_object(id)}, {_, true} <- {:public?, Visibility.is_public?(activity)} do - case format do - _ -> redirect(conn, to: "/notice/#{activity.id}") - end + redirect(conn, to: "/notice/#{activity.id}") else reason when reason in [{:public?, false}, {:activity, nil}] -> {:error, :not_found} @@ -59,13 +53,11 @@ def activity(%{assigns: %{format: format}} = conn, _params) ActivityPubController.call(conn, :activity) end - def activity(%{assigns: %{format: format}} = conn, _params) do + def activity(conn, _params) do with id <- Endpoint.url() <> conn.request_path, {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)}, {_, true} <- {:public?, Visibility.is_public?(activity)} do - case format do - _ -> redirect(conn, to: "/notice/#{activity.id}") - end + redirect(conn, to: "/notice/#{activity.id}") else reason when reason in [{:public?, false}, {:activity, nil}] -> {:error, :not_found} @@ -119,6 +111,7 @@ def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do 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), %{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object, true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index 563edded7..30cf83567 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -8,15 +8,20 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do import Pleroma.Web.ControllerHelper, only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2] - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.RateLimiter alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.MastodonAPI.StatusView + alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.RateLimiter require Pleroma.Constants + plug( + Majic.Plug, + [pool: Pleroma.MajicPool] when action in [:update_avatar, :update_background, :update_banner] + ) + plug( OpenApiSpex.Plug.PutApiSpec, [module: Pleroma.Web.ApiSpec] when action == :confirmation_resend diff --git a/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex b/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex new file mode 100644 index 000000000..dd0a2e22f --- /dev/null +++ b/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.BackupController do + use Pleroma.Web, :controller + + alias Pleroma.User.Backup + alias Pleroma.Web.Plugs.OAuthScopesPlug + + 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) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaBackupOperation + + def index(%{assigns: %{user: user}} = conn, _params) do + backups = Backup.list(user) + render(conn, "index.json", backups: backups) + end + + def create(%{assigns: %{user: user}} = conn, _params) do + with {:ok, _} <- Backup.create(user) do + backups = Backup.list(user) + render(conn, "index.json", backups: backups) + end + end +end diff --git a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex index 867cff829..77564b342 100644 --- a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex @@ -4,17 +4,18 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do use Pleroma.Web, :controller + import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] + alias Pleroma.Activity alias Pleroma.Chat alias Pleroma.Chat.MessageReference alias Pleroma.Object alias Pleroma.Pagination - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.CommonAPI alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView - alias Pleroma.Web.PleromaAPI.ChatView + alias Pleroma.Web.Plugs.OAuthScopesPlug import Ecto.Query @@ -47,7 +48,7 @@ def delete_message(%{assigns: %{user: %{id: user_id} = user}} = conn, %{ }) do with %MessageReference{} = cm_ref <- MessageReference.get_by_id(message_id), - ^chat_id <- cm_ref.chat_id |> to_string(), + ^chat_id <- to_string(cm_ref.chat_id), %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id), {:ok, _} <- remove_or_delete(cm_ref, user) do conn @@ -68,22 +69,18 @@ defp remove_or_delete( end end - defp remove_or_delete(cm_ref, _) do - cm_ref - |> MessageReference.delete() - end + defp remove_or_delete(cm_ref, _), do: MessageReference.delete(cm_ref) def post_chat_message( - %{body_params: params, assigns: %{user: %{id: user_id} = user}} = conn, - %{ - id: id - } + %{body_params: params, assigns: %{user: user}} = conn, + %{id: id} ) do - with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id), + with {:ok, chat} <- Chat.get_by_user_and_id(user, id), %User{} = recipient <- User.get_cached_by_ap_id(chat.recipient), {:ok, activity} <- CommonAPI.post_chat_message(user, recipient, params[:content], - media_id: params[:media_id] + media_id: params[:media_id], + idempotency_key: idempotency_key(conn) ), message <- Object.normalize(activity, false), cm_ref <- MessageReference.for_chat_and_object(chat, message) do @@ -103,13 +100,12 @@ def post_chat_message( end end - def mark_message_as_read(%{assigns: %{user: %{id: user_id}}} = conn, %{ - id: chat_id, - message_id: message_id - }) do - with %MessageReference{} = cm_ref <- - MessageReference.get_by_id(message_id), - ^chat_id <- cm_ref.chat_id |> to_string(), + def mark_message_as_read( + %{assigns: %{user: %{id: user_id}}} = conn, + %{id: chat_id, message_id: message_id} + ) do + with %MessageReference{} = cm_ref <- MessageReference.get_by_id(message_id), + ^chat_id <- to_string(cm_ref.chat_id), %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id), {:ok, cm_ref} <- MessageReference.mark_as_read(cm_ref) do conn @@ -119,66 +115,60 @@ def mark_message_as_read(%{assigns: %{user: %{id: user_id}}} = conn, %{ end def mark_as_read( - %{ - body_params: %{last_read_id: last_read_id}, - assigns: %{user: %{id: user_id}} - } = conn, + %{body_params: %{last_read_id: last_read_id}, assigns: %{user: user}} = conn, %{id: id} ) do - with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id), - {_n, _} <- - MessageReference.set_all_seen_for_chat(chat, last_read_id) do - conn - |> put_view(ChatView) - |> render("show.json", chat: chat) + with {:ok, chat} <- Chat.get_by_user_and_id(user, id), + {_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do + render(conn, "show.json", chat: chat) end end - def messages(%{assigns: %{user: %{id: user_id}}} = conn, %{id: id} = params) do - with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id) do - cm_refs = + def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do + with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do + chat_message_refs = chat |> MessageReference.for_chat_query() |> Pagination.fetch_paginated(params) conn + |> add_link_headers(chat_message_refs) |> put_view(MessageReferenceView) - |> render("index.json", chat_message_references: cm_refs) - else - _ -> - conn - |> put_status(:not_found) - |> json(%{error: "not found"}) + |> render("index.json", chat_message_references: chat_message_refs) end end - def index(%{assigns: %{user: %{id: user_id} = user}} = conn, _params) do - blocked_ap_ids = User.blocked_users_ap_ids(user) + def index(%{assigns: %{user: %{id: user_id} = user}} = conn, params) do + exclude_users = + User.blocked_users_ap_ids(user) ++ + if params[:with_muted], do: [], else: User.muted_users_ap_ids(user) chats = - Chat.for_user_query(user_id) - |> where([c], c.recipient not in ^blocked_ap_ids) + user_id + |> Chat.for_user_query() + |> where([c], c.recipient not in ^exclude_users) |> Repo.all() - conn - |> put_view(ChatView) - |> render("index.json", chats: chats) + render(conn, "index.json", chats: chats) end - def create(%{assigns: %{user: user}} = conn, params) do - with %User{ap_id: recipient} <- User.get_by_id(params[:id]), + def create(%{assigns: %{user: user}} = conn, %{id: id}) do + with %User{ap_id: recipient} <- User.get_cached_by_id(id), {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do - conn - |> put_view(ChatView) - |> render("show.json", chat: chat) + render(conn, "show.json", chat: chat) end end - def show(%{assigns: %{user: user}} = conn, params) do - with %Chat{} = chat <- Repo.get_by(Chat, user_id: user.id, id: params[:id]) do - conn - |> put_view(ChatView) - |> render("show.json", chat: chat) + def show(%{assigns: %{user: user}} = conn, %{id: id}) do + with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do + render(conn, "show.json", chat: chat) + end + end + + defp idempotency_key(conn) do + case get_req_header(conn, "idempotency-key") do + [key] -> key + _ -> nil end end end diff --git a/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex b/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex index 3d007f324..df52b7566 100644 --- a/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex @@ -8,9 +8,9 @@ defmodule Pleroma.Web.PleromaAPI.ConversationController do import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] alias Pleroma.Conversation.Participation - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.MastodonAPI.StatusView + alias Pleroma.Web.Plugs.OAuthScopesPlug plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(:put_view, Pleroma.Web.MastodonAPI.ConversationView) 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 71c53df1d..428c97de6 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.PleromaAPI.EmojiFileController do use Pleroma.Web, :controller @@ -7,7 +11,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do plug(Pleroma.Web.ApiSpec.CastAndValidate) plug( - Pleroma.Plugs.OAuthScopesPlug, + Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["write"], admin: true} when action in [ :create, 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 e3969fee1..a9accc5af 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.PleromaAPI.EmojiPackController do use Pleroma.Web, :controller @@ -6,7 +10,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do plug(Pleroma.Web.ApiSpec.CastAndValidate) plug( - Pleroma.Plugs.OAuthScopesPlug, + Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["write"], admin: true} when action in [ :import_from_filesystem, @@ -18,13 +22,17 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do ] ) - @skip_plugs [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug] - plug(:skip_plug, @skip_plugs when action in [:index, :show, :archive]) + @skip_plugs [ + Pleroma.Web.Plugs.OAuthScopesPlug, + Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug + ] + plug(:skip_plug, @skip_plugs when action in [:index, :archive, :show]) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaEmojiPackOperation - def remote(conn, %{url: url}) do - with {:ok, packs} <- Pack.list_remote(url) do + def remote(conn, params) do + with {:ok, packs} <- + Pack.list_remote(url: params.url, page_size: params.page_size, page: params.page) do json(conn, packs) else {:error, :not_shareable} -> 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 7f9254c13..ae199a50f 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex @@ -7,9 +7,9 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do alias Pleroma.Activity alias Pleroma.Object - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.StatusView + alias Pleroma.Web.Plugs.OAuthScopesPlug plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action in [:create, :delete]) diff --git a/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex new file mode 100644 index 000000000..9e97480df --- /dev/null +++ b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex @@ -0,0 +1,21 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.InstancesController do + use Pleroma.Web, :controller + + alias Pleroma.Instances + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaInstancesOperation + + def show(conn, _params) do + unreachable = + Instances.get_consistently_unreachable() + |> Map.new(fn {host, date} -> {host, to_string(date)} end) + + json(conn, %{"unreachable" => unreachable}) + end +end diff --git a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex index df6c50ca5..15210f1e6 100644 --- a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex @@ -5,10 +5,11 @@ defmodule Pleroma.Web.PleromaAPI.MascotController do use Pleroma.Web, :controller - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.Plugs.OAuthScopesPlug + plug(Majic.Plug, [pool: Pleroma.MajicPool] when action in [:update]) plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action == :show) plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action != :show) @@ -22,14 +23,15 @@ def show(%{assigns: %{user: user}} = conn, _params) do @doc "PUT /api/v1/pleroma/mascot" def update(%{assigns: %{user: user}, body_params: %{file: file}} = conn, _) do - with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)), - # Reject if not an image - %{type: "image"} = attachment <- render_attachment(object) do + with {:content_type, "image" <> _} <- {:content_type, file.content_type}, + {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)) do + attachment = render_attachment(object) {:ok, _user} = User.mascot_update(user, attachment) json(conn, attachment) else - %{type: _} -> render_error(conn, :unsupported_media_type, "mascots can only be images") + {:content_type, _} -> + render_error(conn, :unsupported_media_type, "mascots can only be images") end end diff --git a/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex b/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex index 3ed8bd294..fa32aaa84 100644 --- a/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex @@ -6,10 +6,14 @@ defmodule Pleroma.Web.PleromaAPI.NotificationController do use Pleroma.Web, :controller alias Pleroma.Notification - alias Pleroma.Plugs.OAuthScopesPlug plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :mark_as_read) + + plug( + Pleroma.Web.Plugs.OAuthScopesPlug, + %{scopes: ["write:notifications"]} when action == :mark_as_read + ) + plug(:put_view, Pleroma.Web.MastodonAPI.NotificationView) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaNotificationOperation diff --git a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex index e9a4fba92..632d65434 100644 --- a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex @@ -7,10 +7,10 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleController do import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Plugs.OAuthScopesPlug plug(Pleroma.Web.ApiSpec.CastAndValidate) 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 b86791d09..eba452300 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 @@ -10,8 +10,8 @@ defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationController do alias Pleroma.MFA alias Pleroma.MFA.TOTP - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Web.CommonAPI.Utils + alias Pleroma.Web.Plugs.OAuthScopesPlug plug(OAuthScopesPlug, %{scopes: ["read:security"]} when action in [:settings]) 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 f10c45750..7f089af1c 100644 --- a/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex @@ -7,9 +7,9 @@ defmodule Pleroma.Web.PleromaAPI.UserImportController do require Logger - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.ApiSpec + alias Pleroma.Web.Plugs.OAuthScopesPlug plug(OAuthScopesPlug, %{scopes: ["follow", "write:follows"]} when action == :follow) plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks) diff --git a/lib/pleroma/web/pleroma_api/views/backup_view.ex b/lib/pleroma/web/pleroma_api/views/backup_view.ex new file mode 100644 index 000000000..af75876aa --- /dev/null +++ b/lib/pleroma/web/pleroma_api/views/backup_view.ex @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.BackupView do + use Pleroma.Web, :view + + alias Pleroma.User.Backup + alias Pleroma.Web.CommonAPI.Utils + + def render("show.json", %{backup: %Backup{} = backup}) do + %{ + content_type: backup.content_type, + url: download_url(backup), + file_size: backup.file_size, + processed: backup.processed, + inserted_at: Utils.to_masto_date(backup.inserted_at) + } + end + + def render("index.json", %{backups: backups}) do + render_many(backups, __MODULE__, "show.json") + end + + def download_url(%Backup{file_name: file_name}) do + Pleroma.Web.Endpoint.url() <> "/media/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 d4e08b50d..c058fb340 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 @@ -5,6 +5,7 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do use Pleroma.Web, :view + alias Pleroma.Maps alias Pleroma.User alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.MastodonAPI.StatusView @@ -37,6 +38,7 @@ def render( Pleroma.Web.RichMedia.Helpers.fetch_data_for_object(object) ) } + |> put_idempotency_key() end def render("index.json", opts) do @@ -47,4 +49,13 @@ def render("index.json", opts) do Map.put(opts, :as, :chat_message_reference) ) end + + defp put_idempotency_key(data) 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 + _ -> data + end + end end diff --git a/lib/pleroma/web/plug.ex b/lib/pleroma/web/plug.ex new file mode 100644 index 000000000..840b35072 --- /dev/null +++ b/lib/pleroma/web/plug.ex @@ -0,0 +1,8 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plug do + # Substitute for `call/2` which is defined with `use Pleroma.Web, :plug` + @callback perform(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t() +end diff --git a/lib/pleroma/plugs/admin_secret_authentication_plug.ex b/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex similarity index 89% rename from lib/pleroma/plugs/admin_secret_authentication_plug.ex rename to lib/pleroma/web/plugs/admin_secret_authentication_plug.ex index 2e54df47a..d7d4e4092 100644 --- a/lib/pleroma/plugs/admin_secret_authentication_plug.ex +++ b/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex @@ -2,12 +2,12 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do +defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlug do import Plug.Conn - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.RateLimiter alias Pleroma.User + alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.RateLimiter def init(options) do options diff --git a/lib/pleroma/plugs/authentication_plug.ex b/lib/pleroma/web/plugs/authentication_plug.ex similarity index 94% rename from lib/pleroma/plugs/authentication_plug.ex rename to lib/pleroma/web/plugs/authentication_plug.ex index 057ea42f1..e2a8b1b69 100644 --- a/lib/pleroma/plugs/authentication_plug.ex +++ b/lib/pleroma/web/plugs/authentication_plug.ex @@ -2,8 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.AuthenticationPlug do - alias Pleroma.Plugs.OAuthScopesPlug +defmodule Pleroma.Web.Plugs.AuthenticationPlug do alias Pleroma.User import Plug.Conn @@ -65,7 +64,7 @@ def call( conn |> assign(:user, auth_user) - |> OAuthScopesPlug.skip_plug() + |> Pleroma.Web.Plugs.OAuthScopesPlug.skip_plug() else conn end diff --git a/lib/pleroma/plugs/basic_auth_decoder_plug.ex b/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex similarity index 92% rename from lib/pleroma/plugs/basic_auth_decoder_plug.ex rename to lib/pleroma/web/plugs/basic_auth_decoder_plug.ex index af7ecb0d8..4dadfb000 100644 --- a/lib/pleroma/plugs/basic_auth_decoder_plug.ex +++ b/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.BasicAuthDecoderPlug do +defmodule Pleroma.Web.Plugs.BasicAuthDecoderPlug do import Plug.Conn def init(options) do diff --git a/lib/pleroma/plugs/cache.ex b/lib/pleroma/web/plugs/cache.ex similarity index 96% rename from lib/pleroma/plugs/cache.ex rename to lib/pleroma/web/plugs/cache.ex index f65c2a189..6de01804a 100644 --- a/lib/pleroma/plugs/cache.ex +++ b/lib/pleroma/web/plugs/cache.ex @@ -2,19 +2,19 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.Cache do +defmodule Pleroma.Web.Plugs.Cache do @moduledoc """ Caches successful GET responses. To enable the cache add the plug to a router pipeline or controller: - plug(Pleroma.Plugs.Cache) + plug(Pleroma.Web.Plugs.Cache) ## Configuration To configure the plug you need to pass settings as the second argument to the `plug/2` macro: - plug(Pleroma.Plugs.Cache, [ttl: nil, query_params: true]) + plug(Pleroma.Web.Plugs.Cache, [ttl: nil, query_params: true]) Available options: diff --git a/lib/pleroma/plugs/digest.ex b/lib/pleroma/web/plugs/digest_plug.ex similarity index 100% rename from lib/pleroma/plugs/digest.ex rename to lib/pleroma/web/plugs/digest_plug.ex diff --git a/lib/pleroma/plugs/ensure_authenticated_plug.ex b/lib/pleroma/web/plugs/ensure_authenticated_plug.ex similarity index 94% rename from lib/pleroma/plugs/ensure_authenticated_plug.ex rename to lib/pleroma/web/plugs/ensure_authenticated_plug.ex index 3fe550806..ea2af6881 100644 --- a/lib/pleroma/plugs/ensure_authenticated_plug.ex +++ b/lib/pleroma/web/plugs/ensure_authenticated_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.EnsureAuthenticatedPlug do +defmodule Pleroma.Web.Plugs.EnsureAuthenticatedPlug do import Plug.Conn import Pleroma.Web.TranslationHelpers diff --git a/lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex b/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex similarity index 91% rename from lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex rename to lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex index 7265bb87a..3bebdac6d 100644 --- a/lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex +++ b/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug do +defmodule Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug do import Pleroma.Web.TranslationHelpers import Plug.Conn diff --git a/lib/pleroma/plugs/ensure_user_key_plug.ex b/lib/pleroma/web/plugs/ensure_user_key_plug.ex similarity index 87% rename from lib/pleroma/plugs/ensure_user_key_plug.ex rename to lib/pleroma/web/plugs/ensure_user_key_plug.ex index 9795cdbde..70d3091f0 100644 --- a/lib/pleroma/plugs/ensure_user_key_plug.ex +++ b/lib/pleroma/web/plugs/ensure_user_key_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.EnsureUserKeyPlug do +defmodule Pleroma.Web.Plugs.EnsureUserKeyPlug do import Plug.Conn def init(opts) do diff --git a/lib/pleroma/plugs/expect_authenticated_check_plug.ex b/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex similarity index 71% rename from lib/pleroma/plugs/expect_authenticated_check_plug.ex rename to lib/pleroma/web/plugs/expect_authenticated_check_plug.ex index 66b8d5de5..0925ded4d 100644 --- a/lib/pleroma/plugs/expect_authenticated_check_plug.ex +++ b/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex @@ -2,9 +2,9 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.ExpectAuthenticatedCheckPlug do +defmodule Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug do @moduledoc """ - Marks `Pleroma.Plugs.EnsureAuthenticatedPlug` as expected to be executed later in plug chain. + Marks `Pleroma.Web.Plugs.EnsureAuthenticatedPlug` as expected to be executed later in plug chain. No-op plug which affects `Pleroma.Web` operation (is checked with `PlugHelper.plug_called?/2`). """ diff --git a/lib/pleroma/plugs/expect_public_or_authenticated_check_plug.ex b/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex similarity index 70% rename from lib/pleroma/plugs/expect_public_or_authenticated_check_plug.ex rename to lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex index ba0ef76bd..ace512a78 100644 --- a/lib/pleroma/plugs/expect_public_or_authenticated_check_plug.ex +++ b/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex @@ -2,9 +2,9 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug do +defmodule Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug do @moduledoc """ - Marks `Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug` as expected to be executed later in plug + Marks `Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug` as expected to be executed later in plug chain. No-op plug which affects `Pleroma.Web` operation (is checked with `PlugHelper.plug_called?/2`). diff --git a/lib/pleroma/plugs/federating_plug.ex b/lib/pleroma/web/plugs/federating_plug.ex similarity index 93% rename from lib/pleroma/plugs/federating_plug.ex rename to lib/pleroma/web/plugs/federating_plug.ex index 09038f3c6..3c90a7644 100644 --- a/lib/pleroma/plugs/federating_plug.ex +++ b/lib/pleroma/web/plugs/federating_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.FederatingPlug do +defmodule Pleroma.Web.Plugs.FederatingPlug do import Plug.Conn def init(options) do diff --git a/lib/pleroma/plugs/frontend_static.ex b/lib/pleroma/web/plugs/frontend_static.ex similarity index 62% rename from lib/pleroma/plugs/frontend_static.ex rename to lib/pleroma/web/plugs/frontend_static.ex index 11a0d5382..1b0b36813 100644 --- a/lib/pleroma/plugs/frontend_static.ex +++ b/lib/pleroma/web/plugs/frontend_static.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.FrontendStatic do +defmodule Pleroma.Web.Plugs.FrontendStatic do require Pleroma.Constants @moduledoc """ @@ -34,22 +34,26 @@ def init(opts) do end def call(conn, opts) do - frontend_type = Map.get(opts, :frontend_type, :primary) - path = file_path("", frontend_type) - - if path do - conn - |> call_static(opts, path) + with 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) else - conn + _ -> + conn end end - defp call_static(conn, opts, from) do - opts = - opts - |> Map.put(:from, from) + defp invalid_path?(list) do + invalid_path?(list, :binary.compile_pattern(["/", "\\", ":", "\0"])) + end + 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 call_static(conn, opts, from) do + opts = Map.put(opts, :from, from) Plug.Static.call(conn, opts) end end diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex similarity index 99% rename from lib/pleroma/plugs/http_security_plug.ex rename to lib/pleroma/web/plugs/http_security_plug.ex index c363b193b..45aaf188e 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/web/plugs/http_security_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.HTTPSecurityPlug do +defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do alias Pleroma.Config import Plug.Conn diff --git a/lib/pleroma/plugs/http_signature.ex b/lib/pleroma/web/plugs/http_signature_plug.ex similarity index 100% rename from lib/pleroma/plugs/http_signature.ex rename to lib/pleroma/web/plugs/http_signature_plug.ex diff --git a/lib/pleroma/plugs/idempotency_plug.ex b/lib/pleroma/web/plugs/idempotency_plug.ex similarity index 97% rename from lib/pleroma/plugs/idempotency_plug.ex rename to lib/pleroma/web/plugs/idempotency_plug.ex index f41397075..254a790b0 100644 --- a/lib/pleroma/plugs/idempotency_plug.ex +++ b/lib/pleroma/web/plugs/idempotency_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.IdempotencyPlug do +defmodule Pleroma.Web.Plugs.IdempotencyPlug do import Phoenix.Controller, only: [json: 2] import Plug.Conn diff --git a/lib/pleroma/plugs/instance_static.ex b/lib/pleroma/web/plugs/instance_static.ex similarity index 91% rename from lib/pleroma/plugs/instance_static.ex rename to lib/pleroma/web/plugs/instance_static.ex index 0fb57e422..54b9175df 100644 --- a/lib/pleroma/plugs/instance_static.ex +++ b/lib/pleroma/web/plugs/instance_static.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.InstanceStatic do +defmodule Pleroma.Web.Plugs.InstanceStatic do require Pleroma.Constants @moduledoc """ @@ -16,7 +16,7 @@ def file_path(path) do instance_path = Path.join(Pleroma.Config.get([:instance, :static_dir], "instance/static/"), path) - frontend_path = Pleroma.Plugs.FrontendStatic.file_path(path, :primary) + frontend_path = Pleroma.Web.Plugs.FrontendStatic.file_path(path, :primary) (File.exists?(instance_path) && instance_path) || (frontend_path && File.exists?(frontend_path) && frontend_path) || diff --git a/lib/pleroma/plugs/legacy_authentication_plug.ex b/lib/pleroma/web/plugs/legacy_authentication_plug.ex similarity index 87% rename from lib/pleroma/plugs/legacy_authentication_plug.ex rename to lib/pleroma/web/plugs/legacy_authentication_plug.ex index d346e01a6..2a54d0b59 100644 --- a/lib/pleroma/plugs/legacy_authentication_plug.ex +++ b/lib/pleroma/web/plugs/legacy_authentication_plug.ex @@ -2,10 +2,9 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.LegacyAuthenticationPlug do +defmodule Pleroma.Web.Plugs.LegacyAuthenticationPlug do import Plug.Conn - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User def init(options) do @@ -29,7 +28,7 @@ def call( conn |> assign(:auth_user, user) |> assign(:user, user) - |> OAuthScopesPlug.skip_plug() + |> Pleroma.Web.Plugs.OAuthScopesPlug.skip_plug() else _ -> conn diff --git a/lib/pleroma/plugs/mapped_signature_to_identity_plug.ex b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex similarity index 100% rename from lib/pleroma/plugs/mapped_signature_to_identity_plug.ex rename to lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex diff --git a/lib/pleroma/plugs/oauth_plug.ex b/lib/pleroma/web/plugs/o_auth_plug.ex similarity index 98% rename from lib/pleroma/plugs/oauth_plug.ex rename to lib/pleroma/web/plugs/o_auth_plug.ex index 6fa71ef47..c7b58d90f 100644 --- a/lib/pleroma/plugs/oauth_plug.ex +++ b/lib/pleroma/web/plugs/o_auth_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.OAuthPlug do +defmodule Pleroma.Web.Plugs.OAuthPlug do import Plug.Conn import Ecto.Query diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/web/plugs/o_auth_scopes_plug.ex similarity index 97% rename from lib/pleroma/plugs/oauth_scopes_plug.ex rename to lib/pleroma/web/plugs/o_auth_scopes_plug.ex index b1a736d78..cfc30837c 100644 --- a/lib/pleroma/plugs/oauth_scopes_plug.ex +++ b/lib/pleroma/web/plugs/o_auth_scopes_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.OAuthScopesPlug do +defmodule Pleroma.Web.Plugs.OAuthScopesPlug do import Plug.Conn import Pleroma.Web.Gettext diff --git a/lib/pleroma/plugs/plug_helper.ex b/lib/pleroma/web/plugs/plug_helper.ex similarity index 97% rename from lib/pleroma/plugs/plug_helper.ex rename to lib/pleroma/web/plugs/plug_helper.ex index 9c67be8ef..b314e7596 100644 --- a/lib/pleroma/plugs/plug_helper.ex +++ b/lib/pleroma/web/plugs/plug_helper.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.PlugHelper do +defmodule Pleroma.Web.Plugs.PlugHelper do @moduledoc "Pleroma Plug helper" @called_plugs_list_id :called_plugs diff --git a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex b/lib/pleroma/web/plugs/rate_limiter.ex similarity index 93% rename from lib/pleroma/plugs/rate_limiter/rate_limiter.ex rename to lib/pleroma/web/plugs/rate_limiter.ex index c51e2c634..a589610d1 100644 --- a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex +++ b/lib/pleroma/web/plugs/rate_limiter.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.RateLimiter do +defmodule Pleroma.Web.Plugs.RateLimiter do @moduledoc """ ## Configuration @@ -35,8 +35,8 @@ defmodule Pleroma.Plugs.RateLimiter do AllowedSyntax: - plug(Pleroma.Plugs.RateLimiter, name: :limiter_name) - plug(Pleroma.Plugs.RateLimiter, options) # :name is a required option + plug(Pleroma.Web.Plugs.RateLimiter, name: :limiter_name) + plug(Pleroma.Web.Plugs.RateLimiter, options) # :name is a required option Allowed options: @@ -46,11 +46,11 @@ defmodule Pleroma.Plugs.RateLimiter do Inside a controller: - plug(Pleroma.Plugs.RateLimiter, [name: :one] when action == :one) - plug(Pleroma.Plugs.RateLimiter, [name: :two] when action in [:two, :three]) + plug(Pleroma.Web.Plugs.RateLimiter, [name: :one] when action == :one) + plug(Pleroma.Web.Plugs.RateLimiter, [name: :two] when action in [:two, :three]) plug( - Pleroma.Plugs.RateLimiter, + Pleroma.Web.Plugs.RateLimiter, [name: :status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]] when action in ~w(fav_status unfav_status)a ) @@ -59,7 +59,7 @@ defmodule Pleroma.Plugs.RateLimiter do pipeline :api do ... - plug(Pleroma.Plugs.RateLimiter, name: :one) + plug(Pleroma.Web.Plugs.RateLimiter, name: :one) ... end """ @@ -67,8 +67,8 @@ defmodule Pleroma.Plugs.RateLimiter do import Plug.Conn alias Pleroma.Config - alias Pleroma.Plugs.RateLimiter.LimiterSupervisor alias Pleroma.User + alias Pleroma.Web.Plugs.RateLimiter.LimiterSupervisor require Logger diff --git a/lib/pleroma/plugs/rate_limiter/limiter_supervisor.ex b/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex similarity index 83% rename from lib/pleroma/plugs/rate_limiter/limiter_supervisor.ex rename to lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex index 884268d96..5642bb205 100644 --- a/lib/pleroma/plugs/rate_limiter/limiter_supervisor.ex +++ b/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex @@ -1,4 +1,8 @@ -defmodule Pleroma.Plugs.RateLimiter.LimiterSupervisor do +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.RateLimiter.LimiterSupervisor do use DynamicSupervisor import Cachex.Spec diff --git a/lib/pleroma/plugs/rate_limiter/supervisor.ex b/lib/pleroma/web/plugs/rate_limiter/supervisor.ex similarity index 51% rename from lib/pleroma/plugs/rate_limiter/supervisor.ex rename to lib/pleroma/web/plugs/rate_limiter/supervisor.ex index 9672f7876..a1c84063d 100644 --- a/lib/pleroma/plugs/rate_limiter/supervisor.ex +++ b/lib/pleroma/web/plugs/rate_limiter/supervisor.ex @@ -1,4 +1,8 @@ -defmodule Pleroma.Plugs.RateLimiter.Supervisor do +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.RateLimiter.Supervisor do use Supervisor def start_link(opts) do @@ -7,7 +11,7 @@ def start_link(opts) do def init(_args) do children = [ - Pleroma.Plugs.RateLimiter.LimiterSupervisor + Pleroma.Web.Plugs.RateLimiter.LimiterSupervisor ] opts = [strategy: :one_for_one, name: Pleroma.Web.Streamer.Supervisor] diff --git a/lib/pleroma/web/plugs/remote_ip.ex b/lib/pleroma/web/plugs/remote_ip.ex new file mode 100644 index 000000000..401e2cbfa --- /dev/null +++ b/lib/pleroma/web/plugs/remote_ip.ex @@ -0,0 +1,48 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.RemoteIp do + @moduledoc """ + This is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. + """ + + alias Pleroma.Config + import Plug.Conn + + @behaviour Plug + + def init(_), do: nil + + def call(%{remote_ip: original_remote_ip} = conn, _) do + if Config.get([__MODULE__, :enabled]) do + %{remote_ip: new_remote_ip} = conn = RemoteIp.call(conn, remote_ip_opts()) + assign(conn, :remote_ip_found, original_remote_ip != new_remote_ip) + else + conn + end + end + + defp remote_ip_opts do + headers = Config.get([__MODULE__, :headers], []) |> MapSet.new() + reserved = Config.get([__MODULE__, :reserved], []) + + proxies = + Config.get([__MODULE__, :proxies], []) + |> Enum.concat(reserved) + |> Enum.map(&maybe_add_cidr/1) + + {headers, proxies} + end + + defp maybe_add_cidr(proxy) when is_binary(proxy) do + proxy = + cond do + "/" in String.codepoints(proxy) -> proxy + InetCidr.v4?(InetCidr.parse_address!(proxy)) -> proxy <> "/32" + InetCidr.v6?(InetCidr.parse_address!(proxy)) -> proxy <> "/128" + end + + InetCidr.parse(proxy, true) + end +end diff --git a/lib/pleroma/plugs/session_authentication_plug.ex b/lib/pleroma/web/plugs/session_authentication_plug.ex similarity index 89% rename from lib/pleroma/plugs/session_authentication_plug.ex rename to lib/pleroma/web/plugs/session_authentication_plug.ex index 0f83a5e53..6e176d553 100644 --- a/lib/pleroma/plugs/session_authentication_plug.ex +++ b/lib/pleroma/web/plugs/session_authentication_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.SessionAuthenticationPlug do +defmodule Pleroma.Web.Plugs.SessionAuthenticationPlug do import Plug.Conn def init(options) do diff --git a/lib/pleroma/plugs/set_format_plug.ex b/lib/pleroma/web/plugs/set_format_plug.ex similarity index 92% rename from lib/pleroma/plugs/set_format_plug.ex rename to lib/pleroma/web/plugs/set_format_plug.ex index c03fcb28d..c16d2f81d 100644 --- a/lib/pleroma/plugs/set_format_plug.ex +++ b/lib/pleroma/web/plugs/set_format_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.SetFormatPlug do +defmodule Pleroma.Web.Plugs.SetFormatPlug do import Plug.Conn, only: [assign: 3, fetch_query_params: 1] def init(_), do: nil diff --git a/lib/pleroma/plugs/set_locale_plug.ex b/lib/pleroma/web/plugs/set_locale_plug.ex similarity index 97% rename from lib/pleroma/plugs/set_locale_plug.ex rename to lib/pleroma/web/plugs/set_locale_plug.ex index 9a21d0a9d..d9d24b93f 100644 --- a/lib/pleroma/plugs/set_locale_plug.ex +++ b/lib/pleroma/web/plugs/set_locale_plug.ex @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only # NOTE: this module is based on https://github.com/smeevil/set_locale -defmodule Pleroma.Plugs.SetLocalePlug do +defmodule Pleroma.Web.Plugs.SetLocalePlug do import Plug.Conn, only: [get_req_header: 2, assign: 3] def init(_), do: nil diff --git a/lib/pleroma/plugs/set_user_session_id_plug.ex b/lib/pleroma/web/plugs/set_user_session_id_plug.ex similarity index 87% rename from lib/pleroma/plugs/set_user_session_id_plug.ex rename to lib/pleroma/web/plugs/set_user_session_id_plug.ex index 730c4ac74..e520159e4 100644 --- a/lib/pleroma/plugs/set_user_session_id_plug.ex +++ b/lib/pleroma/web/plugs/set_user_session_id_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.SetUserSessionIdPlug do +defmodule Pleroma.Web.Plugs.SetUserSessionIdPlug do import Plug.Conn alias Pleroma.User diff --git a/lib/pleroma/plugs/static_fe_plug.ex b/lib/pleroma/web/plugs/static_fe_plug.ex similarity index 93% rename from lib/pleroma/plugs/static_fe_plug.ex rename to lib/pleroma/web/plugs/static_fe_plug.ex index 143665c71..658a1052e 100644 --- a/lib/pleroma/plugs/static_fe_plug.ex +++ b/lib/pleroma/web/plugs/static_fe_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.StaticFEPlug do +defmodule Pleroma.Web.Plugs.StaticFEPlug do import Plug.Conn alias Pleroma.Web.StaticFE.StaticFEController diff --git a/lib/pleroma/plugs/trailing_format_plug.ex b/lib/pleroma/web/plugs/trailing_format_plug.ex similarity index 95% rename from lib/pleroma/plugs/trailing_format_plug.ex rename to lib/pleroma/web/plugs/trailing_format_plug.ex index 8b4d5fc9f..e3f57c14a 100644 --- a/lib/pleroma/plugs/trailing_format_plug.ex +++ b/lib/pleroma/web/plugs/trailing_format_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.TrailingFormatPlug do +defmodule Pleroma.Web.Plugs.TrailingFormatPlug do @moduledoc "Calls TrailingFormatPlug for specific paths. Ideally we would just do this in the router, but TrailingFormatPlug needs to be called before Plug.Parsers." @behaviour Plug diff --git a/lib/pleroma/plugs/uploaded_media.ex b/lib/pleroma/web/plugs/uploaded_media.ex similarity index 98% rename from lib/pleroma/plugs/uploaded_media.ex rename to lib/pleroma/web/plugs/uploaded_media.ex index 40984cfc0..402a8bb34 100644 --- a/lib/pleroma/plugs/uploaded_media.ex +++ b/lib/pleroma/web/plugs/uploaded_media.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.UploadedMedia do +defmodule Pleroma.Web.Plugs.UploadedMedia do @moduledoc """ """ diff --git a/lib/pleroma/plugs/user_enabled_plug.ex b/lib/pleroma/web/plugs/user_enabled_plug.ex similarity index 90% rename from lib/pleroma/plugs/user_enabled_plug.ex rename to lib/pleroma/web/plugs/user_enabled_plug.ex index 23e800a74..fa28ee48b 100644 --- a/lib/pleroma/plugs/user_enabled_plug.ex +++ b/lib/pleroma/web/plugs/user_enabled_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.UserEnabledPlug do +defmodule Pleroma.Web.Plugs.UserEnabledPlug do import Plug.Conn alias Pleroma.User diff --git a/lib/pleroma/plugs/user_fetcher_plug.ex b/lib/pleroma/web/plugs/user_fetcher_plug.ex similarity index 91% rename from lib/pleroma/plugs/user_fetcher_plug.ex rename to lib/pleroma/web/plugs/user_fetcher_plug.ex index 235c77d85..4039600da 100644 --- a/lib/pleroma/plugs/user_fetcher_plug.ex +++ b/lib/pleroma/web/plugs/user_fetcher_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.UserFetcherPlug do +defmodule Pleroma.Web.Plugs.UserFetcherPlug do alias Pleroma.User import Plug.Conn diff --git a/lib/pleroma/plugs/user_is_admin_plug.ex b/lib/pleroma/web/plugs/user_is_admin_plug.ex similarity index 91% rename from lib/pleroma/plugs/user_is_admin_plug.ex rename to lib/pleroma/web/plugs/user_is_admin_plug.ex index 488a61d1d..531c965f0 100644 --- a/lib/pleroma/plugs/user_is_admin_plug.ex +++ b/lib/pleroma/web/plugs/user_is_admin_plug.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.UserIsAdminPlug do +defmodule Pleroma.Web.Plugs.UserIsAdminPlug do import Pleroma.Web.TranslationHelpers import Plug.Conn diff --git a/lib/pleroma/web/preload/instance.ex b/lib/pleroma/web/preload/providers/instance.ex similarity index 79% rename from lib/pleroma/web/preload/instance.ex rename to lib/pleroma/web/preload/providers/instance.ex index 50d1f3382..a549bb1eb 100644 --- a/lib/pleroma/web/preload/instance.ex +++ b/lib/pleroma/web/preload/providers/instance.ex @@ -3,15 +3,17 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Preload.Providers.Instance do - alias Pleroma.Plugs.InstanceStatic alias Pleroma.Web.MastodonAPI.InstanceView alias Pleroma.Web.Nodeinfo.Nodeinfo + alias Pleroma.Web.Plugs.InstanceStatic alias Pleroma.Web.Preload.Providers.Provider + alias Pleroma.Web.TwitterAPI.UtilView @behaviour Provider @instance_url "/api/v1/instance" @panel_url "/instance/panel.html" @nodeinfo_url "/nodeinfo/2.0.json" + @fe_config_url "/api/pleroma/frontend_configurations" @impl Provider def generate_terms(_params) do @@ -19,6 +21,7 @@ def generate_terms(_params) do |> build_info_tag() |> build_panel_tag() |> build_nodeinfo_tag() + |> build_fe_config_tag() end defp build_info_tag(acc) do @@ -47,4 +50,10 @@ defp build_nodeinfo_tag(acc) do Map.put(acc, @nodeinfo_url, nodeinfo_data) end end + + defp build_fe_config_tag(acc) do + fe_data = UtilView.render("frontend_configurations.json", %{}) + + Map.put(acc, @fe_config_url, fe_data) + end end diff --git a/lib/pleroma/web/preload/provider.ex b/lib/pleroma/web/preload/providers/provider.ex similarity index 100% rename from lib/pleroma/web/preload/provider.ex rename to lib/pleroma/web/preload/providers/provider.ex diff --git a/lib/pleroma/web/preload/timelines.ex b/lib/pleroma/web/preload/providers/timelines.ex similarity index 100% rename from lib/pleroma/web/preload/timelines.ex rename to lib/pleroma/web/preload/providers/timelines.ex diff --git a/lib/pleroma/web/preload/user.ex b/lib/pleroma/web/preload/providers/user.ex similarity index 100% rename from lib/pleroma/web/preload/user.ex rename to lib/pleroma/web/preload/providers/user.ex diff --git a/lib/pleroma/web/push/push.ex b/lib/pleroma/web/push.ex similarity index 100% rename from lib/pleroma/web/push/push.ex rename to lib/pleroma/web/push.ex diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 16368485e..da535aa68 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -19,7 +19,7 @@ defmodule Pleroma.Web.Push.Impl do @types ["Create", "Follow", "Announce", "Like", "Move"] @doc "Performs sending notifications for user subscriptions" - @spec perform(Notification.t()) :: list(any) | :error + @spec perform(Notification.t()) :: list(any) | :error | {:error, :unknown_type} def perform( %{ activity: %{data: %{"type" => activity_type}} = activity, @@ -64,20 +64,20 @@ def perform(_) do @doc "Push message to web" def push_message(body, sub, api_key, subscription) do case WebPushEncryption.send_web_push(body, sub, api_key) do - {:ok, %{status_code: code}} when 400 <= code and code < 500 -> + {:ok, %{status: code}} when code in 400..499 -> Logger.debug("Removing subscription record") Repo.delete!(subscription) :ok - {:ok, %{status_code: code}} when 200 <= code and code < 300 -> + {:ok, %{status: code}} when code in 200..299 -> :ok - {:ok, %{status_code: code}} -> + {:ok, %{status: code}} -> Logger.error("Web Push Notification failed with code: #{code}") :error - _ -> - Logger.error("Web Push Notification failed with unknown error") + error -> + Logger.error("Web Push Notification failed with #{inspect(error)}") :error end end diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index d7a19df4a..d67b594b5 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -57,7 +57,6 @@ defp get_tld(host) do def fetch_data_for_object(object) do with true <- Config.get([:rich_media, :enabled]), - false <- object.data["sensitive"] || false, {:ok, page_url} <- HTML.extract_first_external_url_from_object(object), :ok <- validate_page_url(page_url), diff --git a/lib/pleroma/web/rich_media/parser/ttl.ex b/lib/pleroma/web/rich_media/parser/ttl.ex new file mode 100644 index 000000000..8353f0fff --- /dev/null +++ b/lib/pleroma/web/rich_media/parser/ttl.ex @@ -0,0 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.RichMedia.Parser.TTL do + @callback ttl(Map.t(), String.t()) :: Integer.t() | nil +end diff --git a/lib/pleroma/web/rich_media/parsers/ttl/aws_signed_url.ex b/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex similarity index 87% rename from lib/pleroma/web/rich_media/parsers/ttl/aws_signed_url.ex rename to lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex index c5aaea2d4..fc4ef79c0 100644 --- a/lib/pleroma/web/rich_media/parsers/ttl/aws_signed_url.ex +++ b/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex @@ -1,7 +1,11 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl do @behaviour Pleroma.Web.RichMedia.Parser.TTL - @impl Pleroma.Web.RichMedia.Parser.TTL + @impl true def ttl(data, _url) do image = Map.get(data, :image) diff --git a/lib/pleroma/web/rich_media/parsers/oembed_parser.ex b/lib/pleroma/web/rich_media/parsers/o_embed.ex similarity index 100% rename from lib/pleroma/web/rich_media/parsers/oembed_parser.ex rename to lib/pleroma/web/rich_media/parsers/o_embed.ex diff --git a/lib/pleroma/web/rich_media/parsers/ttl/ttl.ex b/lib/pleroma/web/rich_media/parsers/ttl/ttl.ex deleted file mode 100644 index 6b3ec6d30..000000000 --- a/lib/pleroma/web/rich_media/parsers/ttl/ttl.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule Pleroma.Web.RichMedia.Parser.TTL do - @callback ttl(Map.t(), String.t()) :: {:ok, Integer.t()} | {:error, String.t()} -end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 42a9db21d..0f0538182 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -5,6 +5,26 @@ defmodule Pleroma.Web.Router do use Pleroma.Web, :router + pipeline :accepts_html do + plug(:accepts, ["html"]) + end + + pipeline :accepts_html_xml do + plug(:accepts, ["html", "xml", "rss", "atom"]) + end + + pipeline :accepts_html_json do + plug(:accepts, ["html", "activity+json", "json"]) + end + + pipeline :accepts_html_xml_json do + plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"]) + end + + pipeline :accepts_xml_rss_atom do + plug(:accepts, ["xml", "rss", "atom"]) + end + pipeline :browser do plug(:accepts, ["html"]) plug(:fetch_session) @@ -12,31 +32,31 @@ defmodule Pleroma.Web.Router do pipeline :oauth do plug(:fetch_session) - plug(Pleroma.Plugs.OAuthPlug) - plug(Pleroma.Plugs.UserEnabledPlug) + plug(Pleroma.Web.Plugs.OAuthPlug) + plug(Pleroma.Web.Plugs.UserEnabledPlug) end pipeline :expect_authentication do - plug(Pleroma.Plugs.ExpectAuthenticatedCheckPlug) + plug(Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug) end pipeline :expect_public_instance_or_authentication do - plug(Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug) + plug(Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug) end pipeline :authenticate do - plug(Pleroma.Plugs.OAuthPlug) - plug(Pleroma.Plugs.BasicAuthDecoderPlug) - plug(Pleroma.Plugs.UserFetcherPlug) - plug(Pleroma.Plugs.SessionAuthenticationPlug) - plug(Pleroma.Plugs.LegacyAuthenticationPlug) - plug(Pleroma.Plugs.AuthenticationPlug) + plug(Pleroma.Web.Plugs.OAuthPlug) + plug(Pleroma.Web.Plugs.BasicAuthDecoderPlug) + plug(Pleroma.Web.Plugs.UserFetcherPlug) + plug(Pleroma.Web.Plugs.SessionAuthenticationPlug) + plug(Pleroma.Web.Plugs.LegacyAuthenticationPlug) + plug(Pleroma.Web.Plugs.AuthenticationPlug) end pipeline :after_auth do - plug(Pleroma.Plugs.UserEnabledPlug) - plug(Pleroma.Plugs.SetUserSessionIdPlug) - plug(Pleroma.Plugs.EnsureUserKeyPlug) + plug(Pleroma.Web.Plugs.UserEnabledPlug) + plug(Pleroma.Web.Plugs.SetUserSessionIdPlug) + plug(Pleroma.Web.Plugs.EnsureUserKeyPlug) end pipeline :base_api do @@ -50,25 +70,25 @@ defmodule Pleroma.Web.Router do plug(:expect_public_instance_or_authentication) plug(:base_api) plug(:after_auth) - plug(Pleroma.Plugs.IdempotencyPlug) + plug(Pleroma.Web.Plugs.IdempotencyPlug) end pipeline :authenticated_api do plug(:expect_authentication) plug(:base_api) plug(:after_auth) - plug(Pleroma.Plugs.EnsureAuthenticatedPlug) - plug(Pleroma.Plugs.IdempotencyPlug) + plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug) + plug(Pleroma.Web.Plugs.IdempotencyPlug) end pipeline :admin_api do plug(:expect_authentication) plug(:base_api) - plug(Pleroma.Plugs.AdminSecretAuthenticationPlug) + plug(Pleroma.Web.Plugs.AdminSecretAuthenticationPlug) plug(:after_auth) - plug(Pleroma.Plugs.EnsureAuthenticatedPlug) - plug(Pleroma.Plugs.UserIsAdminPlug) - plug(Pleroma.Plugs.IdempotencyPlug) + plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug) + plug(Pleroma.Web.Plugs.UserIsAdminPlug) + plug(Pleroma.Web.Plugs.IdempotencyPlug) end pipeline :mastodon_html do @@ -80,7 +100,7 @@ defmodule Pleroma.Web.Router do pipeline :pleroma_html do plug(:browser) plug(:authenticate) - plug(Pleroma.Plugs.EnsureUserKeyPlug) + plug(Pleroma.Web.Plugs.EnsureUserKeyPlug) end pipeline :well_known do @@ -129,16 +149,7 @@ defmodule Pleroma.Web.Router do scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do pipe_through(:admin_api) - post("/users/follow", AdminAPIController, :user_follow) - post("/users/unfollow", AdminAPIController, :user_unfollow) - put("/users/disable_mfa", AdminAPIController, :disable_mfa) - delete("/users", AdminAPIController, :user_delete) - post("/users", AdminAPIController, :users_create) - patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation) - patch("/users/activate", AdminAPIController, :user_activate) - patch("/users/deactivate", AdminAPIController, :user_deactivate) - patch("/users/approve", AdminAPIController, :user_approve) put("/users/tag", AdminAPIController, :tag_users) delete("/users/tag", AdminAPIController, :untag_users) @@ -161,6 +172,15 @@ defmodule Pleroma.Web.Router do :right_delete_multiple ) + post("/users/follow", UserController, :follow) + post("/users/unfollow", UserController, :unfollow) + delete("/users", UserController, :delete) + post("/users", UserController, :create) + patch("/users/:nickname/toggle_activation", UserController, :toggle_activation) + patch("/users/activate", UserController, :activate) + patch("/users/deactivate", UserController, :deactivate) + patch("/users/approve", UserController, :approve) + get("/relay", RelayController, :index) post("/relay", RelayController, :follow) delete("/relay", RelayController, :unfollow) @@ -175,8 +195,8 @@ defmodule Pleroma.Web.Router do get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials) patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials) - get("/users", AdminAPIController, :list_users) - get("/users/:nickname", AdminAPIController, :user_show) + get("/users", UserController, :list) + get("/users/:nickname", UserController, :show) get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses) get("/users/:nickname/chats", AdminAPIController, :list_user_chats) @@ -223,9 +243,25 @@ defmodule Pleroma.Web.Router do get("/chats/:id", ChatController, :show) get("/chats/:id/messages", ChatController, :messages) delete("/chats/:id/messages/:message_id", ChatController, :delete_message) + + post("/backups", AdminAPIController, :create_backup) end scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do + scope "/pack" do + pipe_through(:admin_api) + + post("/", EmojiPackController, :create) + patch("/", EmojiPackController, :update) + delete("/", EmojiPackController, :delete) + end + + scope "/pack" do + pipe_through(:api) + + get("/", EmojiPackController, :show) + end + # Modifying packs scope "/packs" do pipe_through(:admin_api) @@ -234,21 +270,17 @@ defmodule Pleroma.Web.Router do get("/remote", EmojiPackController, :remote) post("/download", EmojiPackController, :download) - post("/:name", EmojiPackController, :create) - patch("/:name", EmojiPackController, :update) - delete("/:name", EmojiPackController, :delete) - - post("/:name/files", EmojiFileController, :create) - patch("/:name/files", EmojiFileController, :update) - delete("/:name/files", EmojiFileController, :delete) + post("/files", EmojiFileController, :create) + patch("/files", EmojiFileController, :update) + delete("/files", EmojiFileController, :delete) end # Pack info / downloading scope "/packs" do pipe_through(:api) + get("/", EmojiPackController, :index) - get("/:name", EmojiPackController, :show) - get("/:name/archive", EmojiPackController, :archive) + get("/archive", EmojiPackController, :archive) end end @@ -343,6 +375,9 @@ defmodule Pleroma.Web.Router do put("/mascot", MascotController, :update) post("/scrobble", ScrobbleController, :create) + + get("/backups", BackupController, :index) + post("/backups", BackupController, :create) end scope [] do @@ -363,6 +398,7 @@ defmodule Pleroma.Web.Router do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do pipe_through(:api) get("/accounts/:id/scrobbles", ScrobbleController, :index) + get("/federation_status", InstancesController, :show) end scope "/api/v1", Pleroma.Web.MastodonAPI do @@ -556,30 +592,43 @@ defmodule Pleroma.Web.Router do ) end - pipeline :ostatus do - plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"]) - plug(Pleroma.Plugs.StaticFEPlug) - end - - pipeline :oembed do - plug(:accepts, ["json", "xml"]) - end - scope "/", Pleroma.Web do - pipe_through([:ostatus, :http_signature]) + # Note: html format is supported only if static FE is enabled + # Note: http signature is only considered for json requests (no auth for non-json requests) + pipe_through([:accepts_html_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug]) get("/objects/:uuid", OStatus.OStatusController, :object) get("/activities/:uuid", OStatus.OStatusController, :activity) get("/notice/:id", OStatus.OStatusController, :notice) - get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player) # Mastodon compatibility routes get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object) get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity) + end + + scope "/", Pleroma.Web do + # Note: html format is supported only if static FE is enabled + # Note: http signature is only considered for json requests (no auth for non-json requests) + pipe_through([:accepts_html_xml_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug]) + + # Note: returns user _profile_ for json requests, redirects to user _feed_ for non-json ones + get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed) + end + + scope "/", Pleroma.Web do + # Note: html format is supported only if static FE is enabled + pipe_through([:accepts_html_xml, Pleroma.Web.Plugs.StaticFEPlug]) get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed) - get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed) + end + scope "/", Pleroma.Web do + pipe_through(:accepts_html) + get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player) + end + + scope "/", Pleroma.Web do + pipe_through(:accepts_xml_rss_atom) get("/tags/:tag", Feed.TagController, :feed, as: :tag_feed) end @@ -727,7 +776,7 @@ defmodule Pleroma.Web.Router do get("/check_password", MongooseIMController, :check_password) end - scope "/", Fallback do + scope "/", Pleroma.Web.Fallback do get("/registration/:token", RedirectController, :registration_page) get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta) get("/api*path", RedirectController, :api_not_implemented) diff --git a/lib/pleroma/web/static_fe/static_fe_controller.ex b/lib/pleroma/web/static_fe/static_fe_controller.ex index a7a891b13..bdec0897a 100644 --- a/lib/pleroma/web/static_fe/static_fe_controller.ex +++ b/lib/pleroma/web/static_fe/static_fe_controller.ex @@ -17,12 +17,96 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do plug(:put_view, Pleroma.Web.StaticFE.StaticFEView) plug(:assign_id) - plug(Pleroma.Plugs.EnsureAuthenticatedPlug, - unless_func: &Pleroma.Web.FederatingPlug.federating?/1 - ) - @page_keys ["max_id", "min_id", "limit", "since_id", "order"] + @doc "Renders requested local public activity or public activities of requested user" + def show(%{assigns: %{notice_id: notice_id}} = conn, _params) do + with %Activity{local: true} = activity <- + Activity.get_by_id_with_object(notice_id), + true <- Visibility.is_public?(activity.object), + {_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)}, + %User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do + meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user}) + + timeline = + activity.object.data["context"] + |> ActivityPub.fetch_activities_for_context(%{}) + |> Enum.reverse() + |> Enum.map(&represent(&1, &1.object.id == activity.object.id)) + + render(conn, "conversation.html", %{activities: timeline, meta: meta}) + else + %Activity{object: %Object{data: data}} -> + conn + |> put_status(:found) + |> redirect(external: data["url"] || data["external_url"] || data["id"]) + + _ -> + not_found(conn, "Post not found.") + end + end + + def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do + with {_, %User{local: true} = user} <- + {:fetch_user, User.get_cached_by_nickname_or_id(username_or_id)}, + {_, :visible} <- {:visibility, User.visible_for(user, _reading_user = nil)} do + meta = Metadata.build_tags(%{user: user}) + + params = + params + |> Map.take(@page_keys) + |> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end) + + timeline = + user + |> ActivityPub.fetch_user_activities(_reading_user = nil, params) + |> Enum.map(&represent/1) + + prev_page_id = + (params["min_id"] || params["max_id"]) && + List.first(timeline) && List.first(timeline).id + + next_page_id = List.last(timeline) && List.last(timeline).id + + render(conn, "profile.html", %{ + user: User.sanitize_html(user), + timeline: timeline, + prev_page_id: prev_page_id, + next_page_id: next_page_id, + meta: meta + }) + else + _ -> + not_found(conn, "User not found.") + end + end + + def show(%{assigns: %{object_id: _}} = conn, _params) do + url = Helpers.url(conn) <> conn.request_path + + case Activity.get_create_by_object_ap_id_with_object(url) do + %Activity{} = activity -> + to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity) + redirect(conn, to: to) + + _ -> + not_found(conn, "Post not found.") + end + end + + def show(%{assigns: %{activity_id: _}} = conn, _params) do + url = Helpers.url(conn) <> conn.request_path + + case Activity.get_by_ap_id(url) do + %Activity{} = activity -> + to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity) + redirect(conn, to: to) + + _ -> + not_found(conn, "Post not found.") + end + end + defp get_title(%Object{data: %{"name" => name}}) when is_binary(name), do: name @@ -81,91 +165,6 @@ defp represent(%Activity{object: %Object{data: data}} = activity, selected) do } end - def show(%{assigns: %{notice_id: notice_id}} = conn, _params) do - with %Activity{local: true} = activity <- - Activity.get_by_id_with_object(notice_id), - true <- Visibility.is_public?(activity.object), - %User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do - meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user}) - - timeline = - activity.object.data["context"] - |> ActivityPub.fetch_activities_for_context(%{}) - |> Enum.reverse() - |> Enum.map(&represent(&1, &1.object.id == activity.object.id)) - - render(conn, "conversation.html", %{activities: timeline, meta: meta}) - else - %Activity{object: %Object{data: data}} -> - conn - |> put_status(:found) - |> redirect(external: data["url"] || data["external_url"] || data["id"]) - - _ -> - not_found(conn, "Post not found.") - end - end - - def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do - case User.get_cached_by_nickname_or_id(username_or_id) do - %User{} = user -> - meta = Metadata.build_tags(%{user: user}) - - params = - params - |> Map.take(@page_keys) - |> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end) - - timeline = - user - |> ActivityPub.fetch_user_activities(nil, params) - |> Enum.map(&represent/1) - - prev_page_id = - (params["min_id"] || params["max_id"]) && - List.first(timeline) && List.first(timeline).id - - next_page_id = List.last(timeline) && List.last(timeline).id - - render(conn, "profile.html", %{ - user: User.sanitize_html(user), - timeline: timeline, - prev_page_id: prev_page_id, - next_page_id: next_page_id, - meta: meta - }) - - _ -> - not_found(conn, "User not found.") - end - end - - def show(%{assigns: %{object_id: _}} = conn, _params) do - url = Helpers.url(conn) <> conn.request_path - - case Activity.get_create_by_object_ap_id_with_object(url) do - %Activity{} = activity -> - to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity) - redirect(conn, to: to) - - _ -> - not_found(conn, "Post not found.") - end - end - - def show(%{assigns: %{activity_id: _}} = conn, _params) do - url = Helpers.url(conn) <> conn.request_path - - case Activity.get_by_ap_id(url) do - %Activity{} = activity -> - to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity) - redirect(conn, to: to) - - _ -> - not_found(conn, "Post not found.") - end - end - defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts), do: assign(conn, :notice_id, notice_id) diff --git a/lib/pleroma/web/streamer/streamer.ex b/lib/pleroma/web/streamer.ex similarity index 96% rename from lib/pleroma/web/streamer/streamer.ex rename to lib/pleroma/web/streamer.ex index 5475f18a6..71fe27c89 100644 --- a/lib/pleroma/web/streamer/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -11,12 +11,12 @@ defmodule Pleroma.Web.Streamer do alias Pleroma.Conversation.Participation alias Pleroma.Notification alias Pleroma.Object - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.CommonAPI alias Pleroma.Web.OAuth.Token + alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.StreamerView @mix_env Mix.env() @@ -57,6 +57,15 @@ def get_topic("hashtag", _user, _oauth_token, %{"tag" => tag} = _params) do {:ok, "hashtag:" <> tag} end + # Allow remote instance streams. + def get_topic("public:remote", _user, _oauth_token, %{"instance" => instance} = _params) do + {:ok, "public:remote:" <> instance} + end + + def get_topic("public:remote:media", _user, _oauth_token, %{"instance" => instance} = _params) do + {:ok, "public:remote:media:" <> instance} + end + # Expand user streams. def get_topic( stream, diff --git a/lib/pleroma/web/templates/layout/app.html.eex b/lib/pleroma/web/templates/layout/app.html.eex index 51603fe0c..3f28f1920 100644 --- a/lib/pleroma/web/templates/layout/app.html.eex +++ b/lib/pleroma/web/templates/layout/app.html.eex @@ -228,7 +228,7 @@

<%= Pleroma.Config.get([:instance, :name]) %>

- <%= render @view_module, @view_template, assigns %> + <%= @inner_content %>
diff --git a/lib/pleroma/web/templates/layout/email_styled.html.eex b/lib/pleroma/web/templates/layout/email_styled.html.eex index ca2caaf4d..82cabd889 100644 --- a/lib/pleroma/web/templates/layout/email_styled.html.eex +++ b/lib/pleroma/web/templates/layout/email_styled.html.eex @@ -181,7 +181,7 @@ <% end %> - <%= render @view_module, @view_template, assigns %> + <%= @inner_content %> diff --git a/lib/pleroma/web/templates/layout/metadata_player.html.eex b/lib/pleroma/web/templates/layout/metadata_player.html.eex index 460f28094..c00f6fa21 100644 --- a/lib/pleroma/web/templates/layout/metadata_player.html.eex +++ b/lib/pleroma/web/templates/layout/metadata_player.html.eex @@ -10,7 +10,7 @@ video, audio { } -<%= render @view_module, @view_template, assigns %> +<%= @inner_content %> diff --git a/lib/pleroma/web/templates/layout/static_fe.html.eex b/lib/pleroma/web/templates/layout/static_fe.html.eex index dc0ee2a5c..e6adb526b 100644 --- a/lib/pleroma/web/templates/layout/static_fe.html.eex +++ b/lib/pleroma/web/templates/layout/static_fe.html.eex @@ -9,7 +9,7 @@
- <%= render @view_module, @view_template, assigns %> + <%= @inner_content %>
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/controller.ex similarity index 96% rename from lib/pleroma/web/twitter_api/twitter_api_controller.ex rename to lib/pleroma/web/twitter_api/controller.ex index c2de26b0b..f42dba442 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/controller.ex @@ -6,10 +6,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do use Pleroma.Web, :controller alias Pleroma.Notification - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.OAuth.Token + alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.TwitterAPI.TokenView require Logger 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 072d889e2..4480a4922 100644 --- a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex @@ -10,7 +10,6 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do alias Pleroma.Activity alias Pleroma.MFA alias Pleroma.Object.Fetcher - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.Auth.Authenticator alias Pleroma.Web.Auth.TOTPAuthenticator @@ -18,11 +17,11 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do @status_types ["Article", "Event", "Note", "Video", "Page", "Question"] - plug(Pleroma.Web.FederatingPlug) + plug(Pleroma.Web.Plugs.FederatingPlug) # Note: follower can submit the form (with password auth) not being signed in (having no token) plug( - OAuthScopesPlug, + Pleroma.Web.Plugs.OAuthScopesPlug, %{fallback: :proceed_unauthenticated, scopes: ["follow", "write:follows"]} when action in [:do_follow] ) diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 70b0fbd54..9ead0d626 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -11,12 +11,12 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do alias Pleroma.Emoji alias Pleroma.Healthcheck alias Pleroma.Notification - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.User alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.WebFinger - plug(Pleroma.Web.FederatingPlug when action == :remote_subscribe) + plug(Pleroma.Web.Plugs.FederatingPlug when action == :remote_subscribe) plug( OAuthScopesPlug, @@ -74,11 +74,7 @@ def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_ end def frontend_configurations(conn, _params) do - config = - Config.get(:frontend_configurations, %{}) - |> Enum.into(%{}) - - json(conn, config) + render(conn, "frontend_configurations.json") end def emoji(conn, _params) do diff --git a/lib/pleroma/web/twitter_api/views/util_view.ex b/lib/pleroma/web/twitter_api/views/util_view.ex index d3bdb4f62..98eea1d18 100644 --- a/lib/pleroma/web/twitter_api/views/util_view.ex +++ b/lib/pleroma/web/twitter_api/views/util_view.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilView do use Pleroma.Web, :view import Phoenix.HTML.Form + alias Pleroma.Config alias Pleroma.Web def status_net_config(instance) do @@ -19,4 +20,9 @@ def status_net_config(instance) do """ end + + def render("frontend_configurations.json", _) do + Config.get(:frontend_configurations, %{}) + |> Enum.into(%{}) + end end diff --git a/lib/pleroma/web/views/email_view.ex b/lib/pleroma/web/views/email_view.ex index 6b0fbe61e..bcdee6571 100644 --- a/lib/pleroma/web/views/email_view.ex +++ b/lib/pleroma/web/views/email_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.EmailView do use Pleroma.Web, :view import Phoenix.HTML diff --git a/lib/pleroma/web/views/mailer/subscription_view.ex b/lib/pleroma/web/views/mailer/subscription_view.ex index fc3d20816..4562a9d6c 100644 --- a/lib/pleroma/web/views/mailer/subscription_view.ex +++ b/lib/pleroma/web/views/mailer/subscription_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Mailer.SubscriptionView do use Pleroma.Web, :view end diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger.ex similarity index 100% rename from lib/pleroma/web/web_finger/web_finger.ex rename to lib/pleroma/web/web_finger.ex diff --git a/lib/pleroma/web/web_finger/web_finger_controller.ex b/lib/pleroma/web/web_finger/web_finger_controller.ex index 7077b20d2..9f0938fc0 100644 --- a/lib/pleroma/web/web_finger/web_finger_controller.ex +++ b/lib/pleroma/web/web_finger/web_finger_controller.ex @@ -7,8 +7,8 @@ defmodule Pleroma.Web.WebFinger.WebFingerController do alias Pleroma.Web.WebFinger - plug(Pleroma.Plugs.SetFormatPlug) - plug(Pleroma.Web.FederatingPlug) + plug(Pleroma.Web.Plugs.SetFormatPlug) + plug(Pleroma.Web.Plugs.FederatingPlug) def host_meta(conn, _params) do xml = WebFinger.host_meta() diff --git a/lib/pleroma/web/xml/xml.ex b/lib/pleroma/web/xml.ex similarity index 100% rename from lib/pleroma/web/xml/xml.ex rename to lib/pleroma/web/xml.ex diff --git a/lib/pleroma/workers/backup_worker.ex b/lib/pleroma/workers/backup_worker.ex new file mode 100644 index 000000000..5b4985983 --- /dev/null +++ b/lib/pleroma/workers/backup_worker.ex @@ -0,0 +1,54 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.BackupWorker do + use Oban.Worker, queue: :backup, max_attempts: 1 + + alias Oban.Job + alias Pleroma.User.Backup + + def process(backup, admin_user_id \\ nil) do + %{"op" => "process", "backup_id" => backup.id, "admin_user_id" => admin_user_id} + |> new() + |> Oban.insert() + end + + def schedule_deletion(backup) do + days = Pleroma.Config.get([Backup, :purge_after_days]) + time = 60 * 60 * 24 * days + scheduled_at = Calendar.NaiveDateTime.add!(backup.inserted_at, time) + + %{"op" => "delete", "backup_id" => backup.id} + |> new(scheduled_at: scheduled_at) + |> Oban.insert() + end + + def delete(backup) do + %{"op" => "delete", "backup_id" => backup.id} + |> new() + |> Oban.insert() + end + + def perform(%Job{ + args: %{"op" => "process", "backup_id" => backup_id, "admin_user_id" => admin_user_id} + }) do + with {:ok, %Backup{} = backup} <- + backup_id |> Backup.get() |> Backup.process(), + {:ok, _job} <- schedule_deletion(backup), + :ok <- Backup.remove_outdated(backup), + {:ok, _} <- + backup + |> Pleroma.Emails.UserEmail.backup_is_ready_email(admin_user_id) + |> Pleroma.Emails.Mailer.deliver() do + {:ok, backup} + end + end + + def perform(%Job{args: %{"op" => "delete", "backup_id" => backup_id}}) do + case Backup.get(backup_id) do + %Backup{} = backup -> Backup.delete(backup) + nil -> :ok + end + end +end diff --git a/lib/xml_builder.ex b/lib/pleroma/xml_builder.ex similarity index 100% rename from lib/xml_builder.ex rename to lib/pleroma/xml_builder.ex diff --git a/mix.exs b/mix.exs index 18f748672..0691902a6 100644 --- a/mix.exs +++ b/mix.exs @@ -114,15 +114,15 @@ defp oauth_deps do # Type `mix help deps` for examples and options. defp deps do [ - {:phoenix, "~> 1.4.17"}, + {:phoenix, "~> 1.5.5"}, {:tzdata, "~> 1.0.3"}, {:plug_cowboy, "~> 2.3"}, - {:phoenix_pubsub, "~> 1.1"}, + {:phoenix_pubsub, "~> 2.0"}, {:phoenix_ecto, "~> 4.0"}, {:ecto_enum, "~> 1.4"}, {:ecto_sql, "~> 3.4.4"}, {:postgrex, ">= 0.15.5"}, - {:oban, "~> 2.0.0"}, + {:oban, "~> 2.1.0"}, {:gettext, "~> 0.18"}, {:pbkdf2_elixir, "~> 1.2"}, {:bcrypt_elixir, "~> 2.2"}, @@ -134,7 +134,7 @@ defp deps do {:cachex, "~> 3.2"}, {:poison, "~> 3.0", override: true}, {:tesla, - git: "https://github.com/teamon/tesla/", + git: "https://github.com/teamon/tesla.git", ref: "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30", override: true}, {:castore, "~> 0.1"}, @@ -165,9 +165,16 @@ defp deps do {:telemetry, "~> 0.3"}, {:poolboy, "~> 1.5"}, {:prometheus, "~> 4.6"}, - {:prometheus_ex, "~> 3.0"}, + {:prometheus_ex, + git: "https://git.pleroma.social/pleroma/elixir-libraries/prometheus.ex.git", + ref: "a4e9beb3c1c479d14b352fd9d6dd7b1f6d7deee5", + override: true}, {:prometheus_plugs, "~> 1.1"}, {:prometheus_phoenix, "~> 1.3"}, + # Note: once `prometheus_phx` is integrated into `prometheus_phoenix`, remove the former: + {:prometheus_phx, + git: "https://git.pleroma.social/pleroma/elixir-libraries/prometheus-phx.git", + branch: "no-logging"}, {:prometheus_ecto, "~> 1.4"}, {:recon, "~> 2.5"}, {:quack, "~> 0.1.1"}, @@ -188,6 +195,8 @@ defp deps do git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", 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"}, diff --git a/mix.lock b/mix.lock index adb3f024a..e5d9bc693 100644 --- a/mix.lock +++ b/mix.lock @@ -18,21 +18,22 @@ "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, "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.0", "92339d4cbadd1e88b5ee43d427b639b68a11071b6f73854e33638e30a0ea11f5", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1fd3b70dce216574ce3c18bdf510b57e7c4c85c2ec9cad4bff854abaf7e58658"}, + "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"]}, "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"}, - "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, + "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.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, "earmark_parser": {:hex, :earmark_parser, "1.4.10", "6603d7a603b9c18d3d20db69921527f82ef09990885ed7525003c7fe7dc86c56", [:mix], [], "hexpm", "8e2d5370b732385db2c9b22215c3f59c84ac7dda7ed7e544d7c459496ae519c0"}, - "ecto": {:hex, :ecto, "3.4.5", "2bcd262f57b2c888b0bd7f7a28c8a48aa11dc1a2c6a858e45dd8f8426d504265", [: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", "8c6d1d4d524559e9b7a062f0498e2c206122552d63eacff0a6567ffe7a8e8691"}, + "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_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.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, + "elixir_make": {:hex, :elixir_make, "0.6.1", "8faa29a5597faba999aeeb72bbb9c91694ef8068f0131192fb199f98d32994ef", [:mix], [], "hexpm", "35d33270680f8d839a4003c3e9f43afb595310a592405a00afc12de4c7f55a18"}, "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"}, @@ -59,12 +60,13 @@ "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"}, "inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"}, - "jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"}, + "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.2.0", "2518bbbea21d2caa9d372424e1ad845b640c6630e2d016f1bd1f518f9ebcca28", [:mix], [], "hexpm", "b8ca8a68b79e30b7938d6c996085f3db14939f29538a59ca5101988bb7f917f6"}, + "majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", "4c692e544b28d1f5e543fb8a44be090f8cd96f80", [branch: "develop"]}, "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"}, @@ -79,28 +81,29 @@ "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.0.0", "e6ce70d94dd46815ec0882a1ffb7356df9a9d5b8a40a64ce5c2536617a447379", [: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", "cf574813bd048b98a698aa587c21367d2e06842d4e1b1993dcd6a696e9e633bd"}, + "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"]}, "p1_utils": {:hex, :p1_utils, "1.0.18", "3fe224de5b2e190d730a3c5da9d6e8540c96484cf4b4692921d1e28f0c32b01c", [:rebar3], [], "hexpm", "1fc8773a71a15553b179c986b22fbeead19b28fe486c332d4929700ffeb71f88"}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, "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.4.17", "1b1bd4cff7cfc87c94deaa7d60dd8c22e04368ab95499483c50640ef3bd838d8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a8e5d7a3d76d452bb5fb86e8b7bd115f737e4f8efe202a463d4aeb4a5809611"}, - "phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"}, + "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"}, "phoenix_html": {:hex, :phoenix_html, "2.14.2", "b8a3899a72050f3f48a36430da507dd99caf0ac2d06c77529b1646964f3d563e", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "58061c8dfd25da5df1ea0ca47c972f161beb6c875cd293917045b92ffe1bf617"}, - "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm", "1f13f9f0f3e769a667a6b6828d29dec37497a082d195cc52dbef401a9b69bf38"}, - "phoenix_swoosh": {:hex, :phoenix_swoosh, "0.3.0", "2acfa0db038a7649e0a4614eee970e6ed9a39d191ccd79a03583b51d0da98165", [:mix], [{:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.0", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "b8bbae4b59a676de6b8bd8675eda37bc8b4424812ae429d6fdcb2b039e00003b"}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"}, + "phoenix_swoosh": {:hex, :phoenix_swoosh, "0.3.2", "43d3518349a22b8b1910ea28b4dd5119926d5017b3187db3fbd1a1e05769a851", [:mix], [{:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.0", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "3e2ac4e883db7af0702d75ba00c19901760e8342b91f8f66e13941de552e777f"}, "plug": {:hex, :plug, "1.10.4", "41eba7d1a2d671faaf531fa867645bd5a3dce0957d8e2a3f398ccff7d2ef017f", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad1e233fe73d2eec56616568d260777b67f53148a999dc2d048f4eb9778fe4a0"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.3.0", "149a50e05cb73c12aad6506a371cd75750c0b19a32f81866e1a323dda9e0e99d", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bc595a1870cef13f9c1e03df56d96804db7f702175e4ccacdb8fc75c02a7b97e"}, - "plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.4.0", "e936ef151751f386804c51f87f7300f5aaae6893cdad726559c3930c6c032948", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e25ddcfc06b1b76e55af79d078b03cbc86bbcb99ce4e5e0a5e4a8114ee039be6"}, + "plug_crypto": {:hex, :plug_crypto, "1.2.0", "1cb20793aa63a6c619dd18bb33d7a3aa94818e5fd39ad357051a67f26dfa2df6", [:mix], [], "hexpm", "a48b538ae8bf381ffac344520755f3007cc10bd8e90b240af98ea29b69683fc2"}, "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.5", "aec40306a622d459b01bff890fa42f1430dac61593b122754144ad9033a2152f", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "ed90c81e1525f65a2ba2279dbcebf030d6d13328daa2f8088b9661eb9143af7f"}, + "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"}, "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"}, - "prometheus_ex": {:hex, :prometheus_ex, "3.0.5", "fa58cfd983487fc5ead331e9a3e0aa622c67232b3ec71710ced122c4c453a02f", [:mix], [{:prometheus, "~> 4.0", [hex: :prometheus, repo: "hexpm", optional: false]}], "hexpm", "9fd13404a48437e044b288b41f76e64acd9735fb8b0e3809f494811dfa66d0fb"}, + "prometheus_ex": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/prometheus.ex.git", "a4e9beb3c1c479d14b352fd9d6dd7b1f6d7deee5", [ref: "a4e9beb3c1c479d14b352fd9d6dd7b1f6d7deee5"]}, "prometheus_phoenix": {:hex, :prometheus_phoenix, "1.3.0", "c4b527e0b3a9ef1af26bdcfbfad3998f37795b9185d475ca610fe4388fdd3bb5", [:mix], [{:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.3 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "c4d1404ac4e9d3d963da601db2a7d8ea31194f0017057fabf0cfb9bf5a6c8c75"}, + "prometheus_phx": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/prometheus-phx.git", "9cd8f248c9381ffedc799905050abce194a97514", [branch: "no-logging"]}, "prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm", "0273a6483ccb936d79ca19b0ab629aef0dba958697c94782bb728b920dfc6a79"}, "quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "d736bfa7444112eb840027bb887832a0e403a4a3437f48028c3b29a2dbbd2543"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, @@ -109,16 +112,16 @@ "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"}, "sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"}, - "swoosh": {:hex, :swoosh, "1.0.0", "c547cfc83f30e12d5d1fdcb623d7de2c2e29a5becfc68bf8f42ba4d23d2c2756", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [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", "b3b08e463f876cb6167f7168e9ad99a069a724e124bcee61847e0e1ed13f4a0d"}, + "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": {:git, "https://github.com/teamon/tesla/", "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30", [ref: "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30"]}, + "tesla": {:git, "https://github.com/teamon/tesla.git", "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30", [ref: "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30"]}, "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"}, "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.3", "73470ad29dde46e350c60a66e6b360d3b99d2d18b74c4c349dbebbc27a09a3eb", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a6e1ee7003c4d04ecbd21dd3ec690d4c6662db5d3bbdd7262d53cdf5e7c746c1"}, + "tzdata": {:hex, :tzdata, "1.0.4", "a3baa4709ea8dba552dca165af6ae97c624a2d6ac14bd265165eaa8e8af94af6", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "b02637db3df1fd66dd2d3c4f194a81633d0e4b44308d36c1b2fdfd1e4e6f169b"}, "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"}, "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", []} + "websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []}, } diff --git a/priv/gettext/zh_Hans/LC_MESSAGES/errors.po b/priv/gettext/zh_Hans/LC_MESSAGES/errors.po index 4f029d558..8b24d4a86 100644 --- a/priv/gettext/zh_Hans/LC_MESSAGES/errors.po +++ b/priv/gettext/zh_Hans/LC_MESSAGES/errors.po @@ -3,8 +3,8 @@ 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-09-20 14:48+0000\n" -"Last-Translator: Kana \n" +"PO-Revision-Date: 2020-10-22 18:25+0000\n" +"Last-Translator: shironeko \n" "Language-Team: Chinese (Simplified) \n" "Language: zh_Hans\n" @@ -49,7 +49,7 @@ msgstr "是被保留的" ## From Ecto.Changeset.validate_confirmation/3 msgid "does not match confirmation" -msgstr "" +msgstr "与验证不符" ## From Ecto.Changeset.no_assoc_constraint/3 msgid "is still associated with this entry" @@ -138,12 +138,12 @@ msgstr "不能获取收藏" #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:438 #, elixir-format msgid "Can't like object" -msgstr "" +msgstr "不能喜欢对象" #: lib/pleroma/web/common_api/utils.ex:563 #, elixir-format msgid "Cannot post an empty status without attachments" -msgstr "" +msgstr "无法发送空白且不包含附件的状态" #: lib/pleroma/web/common_api/utils.ex:511 #, elixir-format @@ -153,100 +153,100 @@ msgstr "" #: lib/pleroma/config/config_db.ex:191 #, elixir-format msgid "Config with params %{params} not found" -msgstr "" +msgstr "无法找到包含参数 %{params} 的配置" #: 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 "" +msgstr "无法删除" #: lib/pleroma/web/common_api/common_api.ex:231 #, elixir-format msgid "Could not favorite" -msgstr "" +msgstr "无法收藏" #: lib/pleroma/web/common_api/common_api.ex:453 #, elixir-format msgid "Could not pin" -msgstr "" +msgstr "无法置顶" #: lib/pleroma/web/common_api/common_api.ex:278 #, elixir-format msgid "Could not unfavorite" -msgstr "" +msgstr "无法取消收藏" #: lib/pleroma/web/common_api/common_api.ex:463 #, elixir-format msgid "Could not unpin" -msgstr "" +msgstr "无法取消置顶" #: lib/pleroma/web/common_api/common_api.ex:216 #, elixir-format msgid "Could not unrepeat" -msgstr "" +msgstr "无法取消转发" #: 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 "" +msgstr "无法更新状态" #: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:207 #, elixir-format msgid "Error." -msgstr "" +msgstr "错误。" #: lib/pleroma/web/twitter_api/twitter_api.ex:106 #, elixir-format msgid "Invalid CAPTCHA" -msgstr "" +msgstr "无效的验证码" #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:116 #: lib/pleroma/web/oauth/oauth_controller.ex:568 #, elixir-format msgid "Invalid credentials" -msgstr "" +msgstr "无效的凭据" #: lib/pleroma/plugs/ensure_authenticated_plug.ex:38 #, elixir-format msgid "Invalid credentials." -msgstr "" +msgstr "无效的凭据。" #: lib/pleroma/web/common_api/common_api.ex:355 #, elixir-format msgid "Invalid indices" -msgstr "" +msgstr "无效的索引" #: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:29 #, elixir-format msgid "Invalid parameters" -msgstr "" +msgstr "无效的参数" #: lib/pleroma/web/common_api/utils.ex:414 #, elixir-format msgid "Invalid password." -msgstr "" +msgstr "无效的密码。" #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:220 #, elixir-format msgid "Invalid request" -msgstr "" +msgstr "无效的请求" #: lib/pleroma/web/twitter_api/twitter_api.ex:109 #, elixir-format msgid "Kocaptcha service unavailable" -msgstr "" +msgstr "Kocaptcha 服务不可用" #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:112 #, elixir-format msgid "Missing parameters" -msgstr "" +msgstr "缺少参数" #: lib/pleroma/web/common_api/utils.ex:547 #, elixir-format msgid "No such conversation" -msgstr "" +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 diff --git a/priv/repo/migrations/20200831114918_remove_unread_conversation_count_from_user.exs b/priv/repo/migrations/20200831114918_remove_unread_conversation_count_from_user.exs new file mode 100644 index 000000000..b7bdb9166 --- /dev/null +++ b/priv/repo/migrations/20200831114918_remove_unread_conversation_count_from_user.exs @@ -0,0 +1,38 @@ +defmodule Pleroma.Repo.Migrations.RemoveUnreadConversationCountFromUser do + use Ecto.Migration + import Ecto.Query + alias Pleroma.Repo + + def up do + alter table(:users) do + remove_if_exists(:unread_conversation_count, :integer) + end + end + + def down do + alter table(:users) do + add_if_not_exists(:unread_conversation_count, :integer, default: 0) + end + + flush() + recalc_unread_conversation_count() + end + + defp recalc_unread_conversation_count do + participations_subquery = + from( + p in "conversation_participations", + where: p.read == false, + group_by: p.user_id, + select: %{user_id: p.user_id, unread_conversation_count: count(p.id)} + ) + + from( + u in "users", + join: p in subquery(participations_subquery), + on: p.user_id == u.id, + update: [set: [unread_conversation_count: p.unread_conversation_count]] + ) + |> Repo.update_all([]) + end +end diff --git a/priv/repo/migrations/20200831115854_add_unread_index_to_conversation_participation.exs b/priv/repo/migrations/20200831115854_add_unread_index_to_conversation_participation.exs new file mode 100644 index 000000000..68771c655 --- /dev/null +++ b/priv/repo/migrations/20200831115854_add_unread_index_to_conversation_participation.exs @@ -0,0 +1,12 @@ +defmodule Pleroma.Repo.Migrations.AddUnreadIndexToConversationParticipation do + use Ecto.Migration + + def change do + create( + index(:conversation_participations, [:user_id], + where: "read = false", + name: "unread_conversation_participation_count_index" + ) + ) + end +end diff --git a/priv/repo/migrations/20200831192323_create_backups.exs b/priv/repo/migrations/20200831192323_create_backups.exs new file mode 100644 index 000000000..3ac5889e2 --- /dev/null +++ b/priv/repo/migrations/20200831192323_create_backups.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.CreateBackups do + use Ecto.Migration + + def change do + create_if_not_exists table(:backups) do + add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) + add(:file_name, :string, null: false) + add(:content_type, :string, null: false) + add(:processed, :boolean, null: false, default: false) + add(:file_size, :bigint) + + timestamps() + end + + create_if_not_exists(index(:backups, [:user_id])) + end +end diff --git a/priv/repo/migrations/20200919182636_remoteip_plug_rename.exs b/priv/repo/migrations/20200919182636_remoteip_plug_rename.exs new file mode 100644 index 000000000..77c3b6db1 --- /dev/null +++ b/priv/repo/migrations/20200919182636_remoteip_plug_rename.exs @@ -0,0 +1,19 @@ +defmodule Pleroma.Repo.Migrations.RemoteipPlugRename do + use Ecto.Migration + + import Ecto.Query + + def up do + config = + from(c in Pleroma.ConfigDB, where: c.group == ^:pleroma and c.key == ^Pleroma.Plugs.RemoteIp) + |> Pleroma.Repo.one() + + if config do + config + |> Ecto.Changeset.change(key: Pleroma.Web.Plugs.RemoteIp) + |> Pleroma.Repo.update() + end + end + + def down, do: :ok +end diff --git a/priv/repo/migrations/20200925065249_make_user_ids_ci.exs b/priv/repo/migrations/20200925065249_make_user_ids_ci.exs new file mode 100644 index 000000000..8ea0f2cf1 --- /dev/null +++ b/priv/repo/migrations/20200925065249_make_user_ids_ci.exs @@ -0,0 +1,9 @@ +defmodule Pleroma.Repo.Migrations.MakeUserIdsCI do + use Ecto.Migration + + def change do + # Migration retired, see + # https://git.pleroma.social/pleroma/pleroma/-/issues/2188 + :noop + end +end diff --git a/priv/repo/migrations/20200928145912_revert_citext_change.exs b/priv/repo/migrations/20200928145912_revert_citext_change.exs new file mode 100644 index 000000000..685a98533 --- /dev/null +++ b/priv/repo/migrations/20200928145912_revert_citext_change.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.RevertCitextChange do + use Ecto.Migration + + def change do + alter table(:users) do + modify(:uri, :text) + end + + # create_if_not_exists(unique_index(:users, :uri)) + end +end diff --git a/priv/repo/migrations/20200930082320_user_ur_is_index_part_three.exs b/priv/repo/migrations/20200930082320_user_ur_is_index_part_three.exs new file mode 100644 index 000000000..816c6526e --- /dev/null +++ b/priv/repo/migrations/20200930082320_user_ur_is_index_part_three.exs @@ -0,0 +1,8 @@ +defmodule Pleroma.Repo.Migrations.UserURIsIndexPartThree do + use Ecto.Migration + + def change do + drop_if_exists(unique_index(:users, :uri)) + create_if_not_exists(index(:users, :uri)) + end +end diff --git a/priv/repo/migrations/20201013141127_refactor_locked_user_field.exs b/priv/repo/migrations/20201013141127_refactor_locked_user_field.exs new file mode 100644 index 000000000..6cd23dbac --- /dev/null +++ b/priv/repo/migrations/20201013141127_refactor_locked_user_field.exs @@ -0,0 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.RefactorLockedUserField do + use Ecto.Migration + + def up do + execute("ALTER TABLE users RENAME COLUMN locked TO is_locked;") + end + + def down do + execute("ALTER TABLE users RENAME COLUMN is_locked TO locked;") + end +end diff --git a/priv/repo/migrations/20201013144052_refactor_discoverable_user_field.exs b/priv/repo/migrations/20201013144052_refactor_discoverable_user_field.exs new file mode 100644 index 000000000..3fdc190cc --- /dev/null +++ b/priv/repo/migrations/20201013144052_refactor_discoverable_user_field.exs @@ -0,0 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.RefactorDiscoverableUserField do + use Ecto.Migration + + def up do + execute("ALTER TABLE users RENAME COLUMN discoverable TO is_discoverable;") + end + + def down do + execute("ALTER TABLE users RENAME COLUMN is_discoverable TO discoverable;") + end +end diff --git a/priv/static/adminfe/app.07a1f8db.css b/priv/static/adminfe/app.6fb984d1.css similarity index 55% rename from priv/static/adminfe/app.07a1f8db.css rename to priv/static/adminfe/app.6fb984d1.css index 9d74d13dc..f1c191c2e 100644 Binary files a/priv/static/adminfe/app.07a1f8db.css and b/priv/static/adminfe/app.6fb984d1.css differ diff --git a/priv/static/adminfe/chunk-0171.aa11eafe.css b/priv/static/adminfe/chunk-0171.aa11eafe.css deleted file mode 100644 index 45340d06b..000000000 Binary files a/priv/static/adminfe/chunk-0171.aa11eafe.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-03c5.f59788cf.css b/priv/static/adminfe/chunk-03c5.f59788cf.css new file mode 100644 index 000000000..863f6f4f4 Binary files /dev/null and b/priv/static/adminfe/chunk-03c5.f59788cf.css differ diff --git a/priv/static/adminfe/chunk-0598.d8f2b478.css b/priv/static/adminfe/chunk-0598.d8f2b478.css deleted file mode 100644 index 9b84800d0..000000000 Binary files a/priv/static/adminfe/chunk-0598.d8f2b478.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-0f09.66ca2a61.css b/priv/static/adminfe/chunk-0f09.66ca2a61.css deleted file mode 100644 index b580e0699..000000000 Binary files a/priv/static/adminfe/chunk-0f09.66ca2a61.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-176e.b7aa5351.css b/priv/static/adminfe/chunk-176e.a3c8376d.css similarity index 100% rename from priv/static/adminfe/chunk-176e.b7aa5351.css rename to priv/static/adminfe/chunk-176e.a3c8376d.css diff --git a/priv/static/adminfe/chunk-19e2.934ad654.css b/priv/static/adminfe/chunk-19e2.934ad654.css deleted file mode 100644 index 4fd86df25..000000000 Binary files a/priv/static/adminfe/chunk-19e2.934ad654.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-28f8.0aae6427.css b/priv/static/adminfe/chunk-28f8.0aae6427.css deleted file mode 100644 index e811b3260..000000000 Binary files a/priv/static/adminfe/chunk-28f8.0aae6427.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-2d97.82cbb623.css b/priv/static/adminfe/chunk-2d97.82cbb623.css deleted file mode 100644 index f6e28e1fb..000000000 Binary files a/priv/static/adminfe/chunk-2d97.82cbb623.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-3221.0ef79c67.css b/priv/static/adminfe/chunk-3221.0ef79c67.css deleted file mode 100644 index bd64e939a..000000000 Binary files a/priv/static/adminfe/chunk-3221.0ef79c67.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-3365.201aa8e6.css b/priv/static/adminfe/chunk-3365.201aa8e6.css new file mode 100644 index 000000000..e5024d666 Binary files /dev/null and b/priv/static/adminfe/chunk-3365.201aa8e6.css differ diff --git a/priv/static/adminfe/chunk-342d.e342722b.css b/priv/static/adminfe/chunk-342d.e342722b.css new file mode 100644 index 000000000..b0fd8dcb3 Binary files /dev/null and b/priv/static/adminfe/chunk-342d.e342722b.css differ diff --git a/priv/static/adminfe/chunk-39ad.ba67c97f.css b/priv/static/adminfe/chunk-39ad.ba67c97f.css deleted file mode 100644 index 778a932cf..000000000 Binary files a/priv/static/adminfe/chunk-39ad.ba67c97f.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-3ba2.63b1228d.css b/priv/static/adminfe/chunk-3ba2.63b1228d.css deleted file mode 100644 index b375f08d5..000000000 Binary files a/priv/static/adminfe/chunk-3ba2.63b1228d.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-4eb4.b72d16c3.css b/priv/static/adminfe/chunk-4eb4.b72d16c3.css deleted file mode 100644 index 1ecdec162..000000000 Binary files a/priv/static/adminfe/chunk-4eb4.b72d16c3.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-521f.b745ee5d.css b/priv/static/adminfe/chunk-521f.b745ee5d.css new file mode 100644 index 000000000..7e8ffb651 Binary files /dev/null and b/priv/static/adminfe/chunk-521f.b745ee5d.css differ diff --git a/priv/static/adminfe/chunk-546f.692d1ab2.css b/priv/static/adminfe/chunk-546f.692d1ab2.css new file mode 100644 index 000000000..5fcb223d8 Binary files /dev/null and b/priv/static/adminfe/chunk-546f.692d1ab2.css differ diff --git a/priv/static/adminfe/chunk-565e.8c036a6e.css b/priv/static/adminfe/chunk-565e.8c036a6e.css deleted file mode 100644 index c126f246e..000000000 Binary files a/priv/static/adminfe/chunk-565e.8c036a6e.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-60a9.7b5b9559.css b/priv/static/adminfe/chunk-60a9.7b5b9559.css deleted file mode 100644 index d45d79f4c..000000000 Binary files a/priv/static/adminfe/chunk-60a9.7b5b9559.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-654e.b2e16b59.css b/priv/static/adminfe/chunk-654d.94689c39.css similarity index 100% rename from priv/static/adminfe/chunk-654e.b2e16b59.css rename to priv/static/adminfe/chunk-654d.94689c39.css diff --git a/priv/static/adminfe/chunk-68ea.81e11186.css b/priv/static/adminfe/chunk-68ea.81e11186.css deleted file mode 100644 index 30bf7de23..000000000 Binary files a/priv/static/adminfe/chunk-68ea.81e11186.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-68ea.7633295f.css b/priv/static/adminfe/chunk-68ea9.dac85813.css similarity index 100% rename from priv/static/adminfe/chunk-68ea.7633295f.css rename to priv/static/adminfe/chunk-68ea9.dac85813.css diff --git a/priv/static/adminfe/chunk-6e81.0e9e6d27.css b/priv/static/adminfe/chunk-6e81.1c0f2da2.css similarity index 100% rename from priv/static/adminfe/chunk-6e81.0e9e6d27.css rename to priv/static/adminfe/chunk-6e81.1c0f2da2.css diff --git a/priv/static/adminfe/chunk-6e81.7e5babfc.css b/priv/static/adminfe/chunk-6e81.7e5babfc.css deleted file mode 100644 index da819ca09..000000000 Binary files a/priv/static/adminfe/chunk-6e81.7e5babfc.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-6e8c.ef26acfd.css b/priv/static/adminfe/chunk-6e8c.ef26acfd.css deleted file mode 100644 index 76f698880..000000000 Binary files a/priv/static/adminfe/chunk-6e8c.ef26acfd.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-7503.cc089ee4.css b/priv/static/adminfe/chunk-7503.cc089ee4.css deleted file mode 100644 index cc1e824b8..000000000 Binary files a/priv/static/adminfe/chunk-7503.cc089ee4.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-0778.29be65e2.css b/priv/static/adminfe/chunk-7c6b.365cbeda.css similarity index 100% rename from priv/static/adminfe/chunk-0778.29be65e2.css rename to priv/static/adminfe/chunk-7c6b.365cbeda.css diff --git a/priv/static/adminfe/chunk-7c6b.b529c720.css b/priv/static/adminfe/chunk-7c6b.b529c720.css deleted file mode 100644 index 9d730019a..000000000 Binary files a/priv/static/adminfe/chunk-7c6b.b529c720.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-40a4.665332db.css b/priv/static/adminfe/chunk-850d.cc4f0ac6.css similarity index 84% rename from priv/static/adminfe/chunk-40a4.665332db.css rename to priv/static/adminfe/chunk-850d.cc4f0ac6.css index 83fefcb55..1cb2ead63 100644 Binary files a/priv/static/adminfe/chunk-40a4.665332db.css and b/priv/static/adminfe/chunk-850d.cc4f0ac6.css differ diff --git a/priv/static/adminfe/chunk-9043.3f527a93.css b/priv/static/adminfe/chunk-9043.3f527a93.css deleted file mode 100644 index d3b7604aa..000000000 Binary files a/priv/static/adminfe/chunk-9043.3f527a93.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-97e2.9f9fab0f.css b/priv/static/adminfe/chunk-97e2.9f9fab0f.css deleted file mode 100644 index d3b7604aa..000000000 Binary files a/priv/static/adminfe/chunk-97e2.9f9fab0f.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-9a72.786caeb3.css b/priv/static/adminfe/chunk-9a72.786caeb3.css deleted file mode 100644 index c0074e6f7..000000000 Binary files a/priv/static/adminfe/chunk-9a72.786caeb3.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-9d55.e2cb1409.css b/priv/static/adminfe/chunk-9d55.e2cb1409.css new file mode 100644 index 000000000..13537842a Binary files /dev/null and b/priv/static/adminfe/chunk-9d55.e2cb1409.css differ diff --git a/priv/static/adminfe/chunk-commons.a343b725.css b/priv/static/adminfe/chunk-commons.c0eb3eb7.css similarity index 100% rename from priv/static/adminfe/chunk-commons.a343b725.css rename to priv/static/adminfe/chunk-commons.c0eb3eb7.css diff --git a/priv/static/adminfe/chunk-6198.3c37d6af.css b/priv/static/adminfe/chunk-d34d.b0dd6fb4.css similarity index 100% rename from priv/static/adminfe/chunk-6198.3c37d6af.css rename to priv/static/adminfe/chunk-d34d.b0dd6fb4.css diff --git a/priv/static/adminfe/chunk-d892.56863b19.css b/priv/static/adminfe/chunk-d892.56863b19.css deleted file mode 100644 index 483d88545..000000000 Binary files a/priv/static/adminfe/chunk-d892.56863b19.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-elementUI.40545a1f.css b/priv/static/adminfe/chunk-elementUI.40545a1f.css deleted file mode 100644 index c802d3a40..000000000 Binary files a/priv/static/adminfe/chunk-elementUI.40545a1f.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-elementUI.f77689d7.css b/priv/static/adminfe/chunk-elementUI.f77689d7.css new file mode 100644 index 000000000..01bdb7fd5 Binary files /dev/null and b/priv/static/adminfe/chunk-elementUI.f77689d7.css differ diff --git a/priv/static/adminfe/chunk-f625.25a6a4ae.css b/priv/static/adminfe/chunk-f625.25a6a4ae.css new file mode 100644 index 000000000..ac26ef0f5 Binary files /dev/null and b/priv/static/adminfe/chunk-f625.25a6a4ae.css differ diff --git a/priv/static/adminfe/chunk-libs.0380664d.css b/priv/static/adminfe/chunk-libs.5cf7f50a.css similarity index 100% rename from priv/static/adminfe/chunk-libs.0380664d.css rename to priv/static/adminfe/chunk-libs.5cf7f50a.css diff --git a/priv/static/adminfe/index.html b/priv/static/adminfe/index.html index 0500424b6..d6b9b22b8 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.1df22cde.js b/priv/static/adminfe/static/js/app.1df22cde.js deleted file mode 100644 index 00a5fbcd3..000000000 Binary files a/priv/static/adminfe/static/js/app.1df22cde.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/app.1df22cde.js.map b/priv/static/adminfe/static/js/app.1df22cde.js.map deleted file mode 100644 index 4f6ad8e95..000000000 Binary files a/priv/static/adminfe/static/js/app.1df22cde.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/app.69891fda.js b/priv/static/adminfe/static/js/app.69891fda.js new file mode 100644 index 000000000..3d04d9273 Binary files /dev/null and b/priv/static/adminfe/static/js/app.69891fda.js differ diff --git a/priv/static/adminfe/static/js/app.69891fda.js.map b/priv/static/adminfe/static/js/app.69891fda.js.map new file mode 100644 index 000000000..0131793e9 Binary files /dev/null and b/priv/static/adminfe/static/js/app.69891fda.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-0171.9e927b8a.js b/priv/static/adminfe/static/js/chunk-0171.9e927b8a.js deleted file mode 100644 index f20f619ad..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0171.9e927b8a.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0171.9e927b8a.js.map b/priv/static/adminfe/static/js/chunk-0171.9e927b8a.js.map deleted file mode 100644 index 4f2d63f3e..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0171.9e927b8a.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-03c5.1c694c49.js b/priv/static/adminfe/static/js/chunk-03c5.1c694c49.js new file mode 100644 index 000000000..b4601abae Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-03c5.1c694c49.js differ diff --git a/priv/static/adminfe/static/js/chunk-03c5.1c694c49.js.map b/priv/static/adminfe/static/js/chunk-03c5.1c694c49.js.map new file mode 100644 index 000000000..193c65bb1 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-03c5.1c694c49.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-16d0.6ce78978.js b/priv/static/adminfe/static/js/chunk-16d0.6ce78978.js deleted file mode 100644 index 497bbcb88..000000000 Binary files a/priv/static/adminfe/static/js/chunk-16d0.6ce78978.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-16d0.6ce78978.js.map b/priv/static/adminfe/static/js/chunk-16d0.6ce78978.js.map deleted file mode 100644 index 17c3378e3..000000000 Binary files a/priv/static/adminfe/static/js/chunk-16d0.6ce78978.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-16d0.fef0ce65.js b/priv/static/adminfe/static/js/chunk-16d0.fef0ce65.js new file mode 100644 index 000000000..8bddbe967 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-16d0.fef0ce65.js differ diff --git a/priv/static/adminfe/static/js/chunk-16d0.fef0ce65.js.map b/priv/static/adminfe/static/js/chunk-16d0.fef0ce65.js.map new file mode 100644 index 000000000..9617ae0d0 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-16d0.fef0ce65.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-176e.5c19378d.js.map b/priv/static/adminfe/static/js/chunk-176e.5c19378d.js.map deleted file mode 100644 index fa116fb3b..000000000 Binary files a/priv/static/adminfe/static/js/chunk-176e.5c19378d.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-176e.5c19378d.js b/priv/static/adminfe/static/js/chunk-176e.be050aba.js similarity index 59% rename from priv/static/adminfe/static/js/chunk-176e.5c19378d.js rename to priv/static/adminfe/static/js/chunk-176e.be050aba.js index 65269ccf1..cab83489b 100644 Binary files a/priv/static/adminfe/static/js/chunk-176e.5c19378d.js and b/priv/static/adminfe/static/js/chunk-176e.be050aba.js differ diff --git a/priv/static/adminfe/static/js/chunk-176e.be050aba.js.map b/priv/static/adminfe/static/js/chunk-176e.be050aba.js.map new file mode 100644 index 000000000..bff959cd6 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-176e.be050aba.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-2d97.079e9e64.js b/priv/static/adminfe/static/js/chunk-2d97.079e9e64.js deleted file mode 100644 index 90399920a..000000000 Binary files a/priv/static/adminfe/static/js/chunk-2d97.079e9e64.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-2d97.079e9e64.js.map b/priv/static/adminfe/static/js/chunk-2d97.079e9e64.js.map deleted file mode 100644 index 5e3e417cd..000000000 Binary files a/priv/static/adminfe/static/js/chunk-2d97.079e9e64.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-3365.b73c30a8.js b/priv/static/adminfe/static/js/chunk-3365.b73c30a8.js new file mode 100644 index 000000000..421bf2a99 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-3365.b73c30a8.js differ diff --git a/priv/static/adminfe/static/js/chunk-3365.b73c30a8.js.map b/priv/static/adminfe/static/js/chunk-3365.b73c30a8.js.map new file mode 100644 index 000000000..d2ad4d9aa Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-3365.b73c30a8.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-342d.479e01dd.js b/priv/static/adminfe/static/js/chunk-342d.479e01dd.js new file mode 100644 index 000000000..5ee311c4a Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-342d.479e01dd.js differ diff --git a/priv/static/adminfe/static/js/chunk-342d.479e01dd.js.map b/priv/static/adminfe/static/js/chunk-342d.479e01dd.js.map new file mode 100644 index 000000000..b73bbb0aa Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-342d.479e01dd.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-40a4.5dc0e299.js b/priv/static/adminfe/static/js/chunk-40a4.5dc0e299.js deleted file mode 100644 index ee0e267db..000000000 Binary files a/priv/static/adminfe/static/js/chunk-40a4.5dc0e299.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-40a4.5dc0e299.js.map b/priv/static/adminfe/static/js/chunk-40a4.5dc0e299.js.map deleted file mode 100644 index 61c30c39b..000000000 Binary files a/priv/static/adminfe/static/js/chunk-40a4.5dc0e299.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-5118.7c48ad58.js b/priv/static/adminfe/static/js/chunk-5118.7c48ad58.js deleted file mode 100644 index 2357e225d..000000000 Binary files a/priv/static/adminfe/static/js/chunk-5118.7c48ad58.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-5118.7c48ad58.js.map b/priv/static/adminfe/static/js/chunk-5118.7c48ad58.js.map deleted file mode 100644 index c29b4b170..000000000 Binary files a/priv/static/adminfe/static/js/chunk-5118.7c48ad58.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-521f.748b331d.js b/priv/static/adminfe/static/js/chunk-521f.748b331d.js new file mode 100644 index 000000000..570dab224 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-521f.748b331d.js differ diff --git a/priv/static/adminfe/static/js/chunk-521f.748b331d.js.map b/priv/static/adminfe/static/js/chunk-521f.748b331d.js.map new file mode 100644 index 000000000..3380bbbd5 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-521f.748b331d.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-546f.81668ba7.js b/priv/static/adminfe/static/js/chunk-546f.81668ba7.js new file mode 100644 index 000000000..252991021 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-546f.81668ba7.js differ diff --git a/priv/static/adminfe/static/js/chunk-546f.81668ba7.js.map b/priv/static/adminfe/static/js/chunk-546f.81668ba7.js.map new file mode 100644 index 000000000..6a9d2a8dc Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-546f.81668ba7.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-565e.e1555105.js b/priv/static/adminfe/static/js/chunk-565e.e1555105.js deleted file mode 100644 index 638c78b38..000000000 Binary files a/priv/static/adminfe/static/js/chunk-565e.e1555105.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-565e.e1555105.js.map b/priv/static/adminfe/static/js/chunk-565e.e1555105.js.map deleted file mode 100644 index 1cfc4cdfa..000000000 Binary files a/priv/static/adminfe/static/js/chunk-565e.e1555105.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-60a9.22fe45f3.js b/priv/static/adminfe/static/js/chunk-60a9.22fe45f3.js deleted file mode 100644 index a23d46b72..000000000 Binary files a/priv/static/adminfe/static/js/chunk-60a9.22fe45f3.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-60a9.22fe45f3.js.map b/priv/static/adminfe/static/js/chunk-60a9.22fe45f3.js.map deleted file mode 100644 index 690979713..000000000 Binary files a/priv/static/adminfe/static/js/chunk-60a9.22fe45f3.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-654e.38dd4b85.js b/priv/static/adminfe/static/js/chunk-654d.653b067f.js similarity index 58% rename from priv/static/adminfe/static/js/chunk-654e.38dd4b85.js rename to priv/static/adminfe/static/js/chunk-654d.653b067f.js index eecdac498..209873ec1 100644 Binary files a/priv/static/adminfe/static/js/chunk-654e.38dd4b85.js and b/priv/static/adminfe/static/js/chunk-654d.653b067f.js differ diff --git a/priv/static/adminfe/static/js/chunk-654d.653b067f.js.map b/priv/static/adminfe/static/js/chunk-654d.653b067f.js.map new file mode 100644 index 000000000..72aca0e98 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-654d.653b067f.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-654e.38dd4b85.js.map b/priv/static/adminfe/static/js/chunk-654e.38dd4b85.js.map deleted file mode 100644 index 4fc105fb7..000000000 Binary files a/priv/static/adminfe/static/js/chunk-654e.38dd4b85.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-68ea.0dae7e55.js b/priv/static/adminfe/static/js/chunk-68ea.0dae7e55.js deleted file mode 100644 index dc31a8bb0..000000000 Binary files a/priv/static/adminfe/static/js/chunk-68ea.0dae7e55.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-68ea.0dae7e55.js.map b/priv/static/adminfe/static/js/chunk-68ea.0dae7e55.js.map deleted file mode 100644 index 6c87803a8..000000000 Binary files a/priv/static/adminfe/static/js/chunk-68ea.0dae7e55.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-68ea.6d56674a.js b/priv/static/adminfe/static/js/chunk-68ea.6d56674a.js new file mode 100644 index 000000000..1f43a39db Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-68ea.6d56674a.js differ diff --git a/priv/static/adminfe/static/js/chunk-68ea.6d56674a.js.map b/priv/static/adminfe/static/js/chunk-68ea.6d56674a.js.map new file mode 100644 index 000000000..45000a1b0 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-68ea.6d56674a.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-68ea9.9821cd6a.js b/priv/static/adminfe/static/js/chunk-68ea9.9821cd6a.js new file mode 100644 index 000000000..02091ed84 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-68ea9.9821cd6a.js differ diff --git a/priv/static/adminfe/static/js/chunk-68ea9.9821cd6a.js.map b/priv/static/adminfe/static/js/chunk-68ea9.9821cd6a.js.map new file mode 100644 index 000000000..019cede66 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-68ea9.9821cd6a.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-6e81.6043af74.js b/priv/static/adminfe/static/js/chunk-6e81.ebe9039f.js similarity index 97% rename from priv/static/adminfe/static/js/chunk-6e81.6043af74.js rename to priv/static/adminfe/static/js/chunk-6e81.ebe9039f.js index 82b08ad24..cd79db1d3 100644 Binary files a/priv/static/adminfe/static/js/chunk-6e81.6043af74.js and b/priv/static/adminfe/static/js/chunk-6e81.ebe9039f.js differ diff --git a/priv/static/adminfe/static/js/chunk-6e81.6043af74.js.map b/priv/static/adminfe/static/js/chunk-6e81.ebe9039f.js.map similarity index 98% rename from priv/static/adminfe/static/js/chunk-6e81.6043af74.js.map rename to priv/static/adminfe/static/js/chunk-6e81.ebe9039f.js.map index 2c1c86e2c..10b437760 100644 Binary files a/priv/static/adminfe/static/js/chunk-6e81.6043af74.js.map and b/priv/static/adminfe/static/js/chunk-6e81.ebe9039f.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-6e8c.2aa335e0.js b/priv/static/adminfe/static/js/chunk-6e8c.2aa335e0.js deleted file mode 100644 index 020158f81..000000000 Binary files a/priv/static/adminfe/static/js/chunk-6e8c.2aa335e0.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-6e8c.2aa335e0.js.map b/priv/static/adminfe/static/js/chunk-6e8c.2aa335e0.js.map deleted file mode 100644 index 1e742c3f9..000000000 Binary files a/priv/static/adminfe/static/js/chunk-6e8c.2aa335e0.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-7503.278e0031.js b/priv/static/adminfe/static/js/chunk-7503.278e0031.js deleted file mode 100644 index 3875f9ad9..000000000 Binary files a/priv/static/adminfe/static/js/chunk-7503.278e0031.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-7503.278e0031.js.map b/priv/static/adminfe/static/js/chunk-7503.278e0031.js.map deleted file mode 100644 index 494d1a763..000000000 Binary files a/priv/static/adminfe/static/js/chunk-7503.278e0031.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-7c6b.1ebeb0e4.js b/priv/static/adminfe/static/js/chunk-7c6b.1ebeb0e4.js deleted file mode 100644 index 63be4d84f..000000000 Binary files a/priv/static/adminfe/static/js/chunk-7c6b.1ebeb0e4.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-7c6b.1ebeb0e4.js.map b/priv/static/adminfe/static/js/chunk-7c6b.1ebeb0e4.js.map deleted file mode 100644 index 85d8dcb1c..000000000 Binary files a/priv/static/adminfe/static/js/chunk-7c6b.1ebeb0e4.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-7c6b.56a14571.js b/priv/static/adminfe/static/js/chunk-7c6b.56a14571.js new file mode 100644 index 000000000..df9b7d6ac Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7c6b.56a14571.js differ diff --git a/priv/static/adminfe/static/js/chunk-7c6b.56a14571.js.map b/priv/static/adminfe/static/js/chunk-7c6b.56a14571.js.map new file mode 100644 index 000000000..6584ba082 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7c6b.56a14571.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-850d.3e6102c2.js b/priv/static/adminfe/static/js/chunk-850d.3e6102c2.js new file mode 100644 index 000000000..a2c2df2e7 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-850d.3e6102c2.js differ diff --git a/priv/static/adminfe/static/js/chunk-850d.3e6102c2.js.map b/priv/static/adminfe/static/js/chunk-850d.3e6102c2.js.map new file mode 100644 index 000000000..7f7718547 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-850d.3e6102c2.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-97e2.c51fe6b0.js b/priv/static/adminfe/static/js/chunk-97e2.c51fe6b0.js deleted file mode 100644 index a9cef7b6b..000000000 Binary files a/priv/static/adminfe/static/js/chunk-97e2.c51fe6b0.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-97e2.c51fe6b0.js.map b/priv/static/adminfe/static/js/chunk-97e2.c51fe6b0.js.map deleted file mode 100644 index 1d489f4c2..000000000 Binary files a/priv/static/adminfe/static/js/chunk-97e2.c51fe6b0.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-9a72.41e843cd.js b/priv/static/adminfe/static/js/chunk-9a72.41e843cd.js deleted file mode 100644 index 575a01d30..000000000 Binary files a/priv/static/adminfe/static/js/chunk-9a72.41e843cd.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-9a72.41e843cd.js.map b/priv/static/adminfe/static/js/chunk-9a72.41e843cd.js.map deleted file mode 100644 index aede70d0a..000000000 Binary files a/priv/static/adminfe/static/js/chunk-9a72.41e843cd.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-9d55.7af22f45.js b/priv/static/adminfe/static/js/chunk-9d55.7af22f45.js new file mode 100644 index 000000000..89d35af79 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-9d55.7af22f45.js differ diff --git a/priv/static/adminfe/static/js/chunk-9d55.7af22f45.js.map b/priv/static/adminfe/static/js/chunk-9d55.7af22f45.js.map new file mode 100644 index 000000000..fa8694b8e Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-9d55.7af22f45.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-commons.51fe2926.js.map b/priv/static/adminfe/static/js/chunk-commons.51fe2926.js.map deleted file mode 100644 index 7d55c69d6..000000000 Binary files a/priv/static/adminfe/static/js/chunk-commons.51fe2926.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-commons.51fe2926.js b/priv/static/adminfe/static/js/chunk-commons.a6002038.js similarity index 89% rename from priv/static/adminfe/static/js/chunk-commons.51fe2926.js rename to priv/static/adminfe/static/js/chunk-commons.a6002038.js index 3fe10f0d3..2b16da9c7 100644 Binary files a/priv/static/adminfe/static/js/chunk-commons.51fe2926.js and b/priv/static/adminfe/static/js/chunk-commons.a6002038.js differ diff --git a/priv/static/adminfe/static/js/chunk-commons.a6002038.js.map b/priv/static/adminfe/static/js/chunk-commons.a6002038.js.map new file mode 100644 index 000000000..3c7d78861 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-commons.a6002038.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-d34d.0f06fe76.js b/priv/static/adminfe/static/js/chunk-d34d.0f06fe76.js new file mode 100644 index 000000000..edd221e6e Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-d34d.0f06fe76.js differ diff --git a/priv/static/adminfe/static/js/chunk-d34d.0f06fe76.js.map b/priv/static/adminfe/static/js/chunk-d34d.0f06fe76.js.map new file mode 100644 index 000000000..6bcd4ed8b Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-d34d.0f06fe76.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-d55e.f9bab96d.js b/priv/static/adminfe/static/js/chunk-d55e.f9bab96d.js new file mode 100644 index 000000000..4e7ab35bb Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-d55e.f9bab96d.js differ diff --git a/priv/static/adminfe/static/js/chunk-d55e.f9bab96d.js.map b/priv/static/adminfe/static/js/chunk-d55e.f9bab96d.js.map new file mode 100644 index 000000000..323d9ba2a Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-d55e.f9bab96d.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-df62.6c5105a6.js b/priv/static/adminfe/static/js/chunk-df62.6c5105a6.js deleted file mode 100644 index c6c4b82ee..000000000 Binary files a/priv/static/adminfe/static/js/chunk-df62.6c5105a6.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-df62.6c5105a6.js.map b/priv/static/adminfe/static/js/chunk-df62.6c5105a6.js.map deleted file mode 100644 index a2380c4cd..000000000 Binary files a/priv/static/adminfe/static/js/chunk-df62.6c5105a6.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-elementUI.21957ec8.js b/priv/static/adminfe/static/js/chunk-elementUI.21957ec8.js new file mode 100644 index 000000000..65462dc89 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-elementUI.21957ec8.js differ diff --git a/priv/static/adminfe/static/js/chunk-elementUI.21957ec8.js.map b/priv/static/adminfe/static/js/chunk-elementUI.21957ec8.js.map new file mode 100644 index 000000000..3381fd56a Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-elementUI.21957ec8.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-elementUI.8e5c404c.js b/priv/static/adminfe/static/js/chunk-elementUI.8e5c404c.js deleted file mode 100644 index e8424c9ed..000000000 Binary files a/priv/static/adminfe/static/js/chunk-elementUI.8e5c404c.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-elementUI.8e5c404c.js.map b/priv/static/adminfe/static/js/chunk-elementUI.8e5c404c.js.map deleted file mode 100644 index a3c9be946..000000000 Binary files a/priv/static/adminfe/static/js/chunk-elementUI.8e5c404c.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-f625.29237434.js b/priv/static/adminfe/static/js/chunk-f625.29237434.js new file mode 100644 index 000000000..522755a98 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-f625.29237434.js differ diff --git a/priv/static/adminfe/static/js/chunk-f625.29237434.js.map b/priv/static/adminfe/static/js/chunk-f625.29237434.js.map new file mode 100644 index 000000000..4f8774c3a Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-f625.29237434.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-libs.32ea9181.js b/priv/static/adminfe/static/js/chunk-libs.32ea9181.js new file mode 100644 index 000000000..29cfb2b1d Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-libs.32ea9181.js differ diff --git a/priv/static/adminfe/static/js/chunk-libs.32ea9181.js.map b/priv/static/adminfe/static/js/chunk-libs.32ea9181.js.map new file mode 100644 index 000000000..c80cf9acc Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-libs.32ea9181.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-libs.f842b12e.js b/priv/static/adminfe/static/js/chunk-libs.f842b12e.js deleted file mode 100644 index 2e8e0aba5..000000000 Binary files a/priv/static/adminfe/static/js/chunk-libs.f842b12e.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-libs.f842b12e.js.map b/priv/static/adminfe/static/js/chunk-libs.f842b12e.js.map deleted file mode 100644 index de17844c9..000000000 Binary files a/priv/static/adminfe/static/js/chunk-libs.f842b12e.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/oAJy.2d5429b2.js b/priv/static/adminfe/static/js/oAJy.2d5429b2.js new file mode 100644 index 000000000..72ef1a89b Binary files /dev/null and b/priv/static/adminfe/static/js/oAJy.2d5429b2.js differ diff --git a/priv/static/adminfe/static/js/oAJy.2d5429b2.js.map b/priv/static/adminfe/static/js/oAJy.2d5429b2.js.map new file mode 100644 index 000000000..e93954508 Binary files /dev/null and b/priv/static/adminfe/static/js/oAJy.2d5429b2.js.map differ diff --git a/priv/static/adminfe/static/js/oAJy.840fb1c2.js b/priv/static/adminfe/static/js/oAJy.840fb1c2.js deleted file mode 100644 index 9973db60a..000000000 Binary files a/priv/static/adminfe/static/js/oAJy.840fb1c2.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/oAJy.840fb1c2.js.map b/priv/static/adminfe/static/js/oAJy.840fb1c2.js.map deleted file mode 100644 index 48420eecd..000000000 Binary files a/priv/static/adminfe/static/js/oAJy.840fb1c2.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.04c4fa2f.js b/priv/static/adminfe/static/js/runtime.04c4fa2f.js deleted file mode 100644 index c08585729..000000000 Binary files a/priv/static/adminfe/static/js/runtime.04c4fa2f.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.04c4fa2f.js.map b/priv/static/adminfe/static/js/runtime.04c4fa2f.js.map deleted file mode 100644 index bf0af11e1..000000000 Binary files a/priv/static/adminfe/static/js/runtime.04c4fa2f.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.8f631d12.js b/priv/static/adminfe/static/js/runtime.8f631d12.js new file mode 100644 index 000000000..6fa7d9ee1 Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.8f631d12.js differ diff --git a/priv/static/adminfe/static/js/runtime.8f631d12.js.map b/priv/static/adminfe/static/js/runtime.8f631d12.js.map new file mode 100644 index 000000000..d5c20400e Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.8f631d12.js.map differ diff --git a/priv/static/favicon.png b/priv/static/favicon.png index a96d5d252..098040a00 100644 Binary files a/priv/static/favicon.png and b/priv/static/favicon.png differ diff --git a/priv/templates/sample_config.eex b/priv/templates/sample_config.eex index bc7e37375..cdddc47ea 100644 --- a/priv/templates/sample_config.eex +++ b/priv/templates/sample_config.eex @@ -71,3 +71,7 @@ config :pleroma, Pleroma.Uploaders.Local, uploads: "<%= uploads_dir %>" config :joken, default_signer: "<%= jwt_secret %>" config :pleroma, configurable_from_database: <%= db_configurable? %> + +<%= if Kernel.length(upload_filters) > 0 do +"config :pleroma, Pleroma.Upload, filters: #{inspect(upload_filters)}" +end %> diff --git a/test/credo/check/consistency/file_location.ex b/test/credo/check/consistency/file_location.ex new file mode 100644 index 000000000..500983608 --- /dev/null +++ b/test/credo/check/consistency/file_location.ex @@ -0,0 +1,166 @@ +# 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 +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Credo.Check.Consistency.FileLocation do + @moduledoc false + + # credo:disable-for-this-file Credo.Check.Readability.Specs + + @checkdoc """ + File location should follow the namespace hierarchy of the module it defines. + + Examples: + + - `lib/my_system.ex` should define the `MySystem` module + - `lib/my_system/accounts.ex` should define the `MySystem.Accounts` module + """ + @explanation [warning: @checkdoc] + + @special_namespaces [ + "controllers", + "views", + "operations", + "channels" + ] + + # `use Credo.Check` required that module attributes are already defined, so we need + # to place these attributes + # before use/alias expressions. + # credo:disable-for-next-line VBT.Credo.Check.Consistency.ModuleLayout + use Credo.Check, category: :warning, base_priority: :high + + alias Credo.Code + + def run(source_file, params \\ []) do + case verify(source_file, params) do + :ok -> + [] + + {:error, module, expected_file} -> + error(IssueMeta.for(source_file, params), module, expected_file) + end + end + + defp verify(source_file, params) do + source_file.filename + |> Path.relative_to_cwd() + |> verify(Code.ast(source_file), params) + end + + @doc false + def verify(relative_path, ast, params) do + if verify_path?(relative_path, params), + do: ast |> main_module() |> verify_module(relative_path, params), + else: :ok + end + + defp verify_path?(relative_path, params) do + case Path.split(relative_path) do + ["lib" | _] -> not exclude?(relative_path, params) + ["test", "support" | _] -> false + ["test", "test_helper.exs"] -> false + ["test" | _] -> not exclude?(relative_path, params) + _ -> false + end + end + + defp exclude?(relative_path, params) do + params + |> Keyword.get(:exclude, []) + |> Enum.any?(&String.starts_with?(relative_path, &1)) + end + + defp main_module(ast) do + {_ast, modules} = Macro.prewalk(ast, [], &traverse/2) + Enum.at(modules, -1) + end + + defp traverse({:defmodule, _meta, args}, modules) do + [{:__aliases__, _, name_parts}, _module_body] = args + {args, [Module.concat(name_parts) | modules]} + end + + defp traverse(ast, state), do: {ast, state} + + # empty file - shouldn't really happen, but we'll let it through + defp verify_module(nil, _relative_path, _params), do: :ok + + defp verify_module(main_module, relative_path, params) do + parsed_path = parsed_path(relative_path, params) + + expected_file = + expected_file_base(parsed_path.root, main_module) <> + Path.extname(parsed_path.allowed) + + cond do + expected_file == parsed_path.allowed -> + :ok + + special_namespaces?(parsed_path.allowed) -> + original_path = parsed_path.allowed + + namespace = + Enum.find(@special_namespaces, original_path, fn namespace -> + String.contains?(original_path, namespace) + end) + + allowed = String.replace(original_path, "/" <> namespace, "") + + if expected_file == allowed, + do: :ok, + else: {:error, main_module, expected_file} + + true -> + {:error, main_module, expected_file} + end + end + + defp special_namespaces?(path), do: String.contains?(path, @special_namespaces) + + defp parsed_path(relative_path, params) do + parts = Path.split(relative_path) + + allowed = + Keyword.get(params, :ignore_folder_namespace, %{}) + |> Stream.flat_map(fn {root, folders} -> Enum.map(folders, &Path.join([root, &1])) end) + |> Stream.map(&Path.split/1) + |> Enum.find(&List.starts_with?(parts, &1)) + |> case do + nil -> + relative_path + + ignore_parts -> + Stream.drop(ignore_parts, -1) + |> Enum.concat(Stream.drop(parts, length(ignore_parts))) + |> Path.join() + end + + %{root: hd(parts), allowed: allowed} + end + + defp expected_file_base(root_folder, module) do + {parent_namespace, module_name} = module |> Module.split() |> Enum.split(-1) + + relative_path = + if parent_namespace == [], + do: "", + else: parent_namespace |> Module.concat() |> Macro.underscore() + + file_name = module_name |> Module.concat() |> Macro.underscore() + + Path.join([root_folder, relative_path, file_name]) + end + + defp error(issue_meta, module, expected_file) do + format_issue(issue_meta, + message: + "Mismatch between file name and main module #{inspect(module)}. " <> + "Expected file path to be #{expected_file}. " <> + "Either move the file or rename the module.", + line_no: 1 + ) + end +end diff --git a/test/fixtures/config/temp.secret.exs b/test/fixtures/config/temp.secret.exs index fa8c7c7e8..621bc8cf6 100644 --- a/test/fixtures/config/temp.secret.exs +++ b/test/fixtures/config/temp.secret.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + use Mix.Config config :pleroma, :first_setting, key: "value", key2: [Pleroma.Repo] diff --git a/test/fixtures/mastodon-post-activity-nsfw.json b/test/fixtures/mastodon-post-activity-nsfw.json new file mode 100644 index 000000000..70729a1bd --- /dev/null +++ b/test/fixtures/mastodon-post-activity-nsfw.json @@ -0,0 +1,68 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "Emoji": "toot:Emoji", + "Hashtag": "as:Hashtag", + "atomUri": "ostatus:atomUri", + "conversation": "ostatus:conversation", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "movedTo": "as:movedTo", + "ostatus": "http://ostatus.org#", + "toot": "http://joinmastodon.org/ns#" + } + ], + "actor": "http://mastodon.example.org/users/admin", + "cc": [ + "http://mastodon.example.org/users/admin/followers", + "http://localtesting.pleroma.lol/users/lain" + ], + "id": "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity", + "nickname": "lain", + "object": { + "atomUri": "http://mastodon.example.org/users/admin/statuses/99512778738411822", + "attachment": [], + "attributedTo": "http://mastodon.example.org/users/admin", + "cc": [ + "http://mastodon.example.org/users/admin/followers", + "http://localtesting.pleroma.lol/users/lain" + ], + "content": "

@lain #moo

", + "conversation": "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation", + "id": "http://mastodon.example.org/users/admin/statuses/99512778738411822", + "inReplyTo": null, + "inReplyToAtomUri": null, + "published": "2018-02-12T14:08:20Z", + "summary": "cw", + "tag": [ + { + "href": "http://localtesting.pleroma.lol/users/lain", + "name": "@lain@localtesting.pleroma.lol", + "type": "Mention" + }, + { + "href": "http://mastodon.example.org/tags/nsfw", + "name": "#NSFW", + "type": "Hashtag" + } + ], + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "type": "Note", + "url": "http://mastodon.example.org/@admin/99512778738411822" + }, + "published": "2018-02-12T14:08:20Z", + "signature": { + "created": "2018-02-12T14:08:20Z", + "creator": "http://mastodon.example.org/users/admin#main-key", + "signatureValue": "rnNfcopkc6+Ju73P806popcfwrK9wGYHaJVG1/ZvrlEbWVDzaHjkXqj9Q3/xju5l8CSn9tvSgCCtPFqZsFQwn/pFIFUcw7ZWB2xi4bDm3NZ3S4XQ8JRaaX7og5hFxAhWkGhJhAkfxVnOg2hG+w2d/7d7vRVSC1vo5ip4erUaA/PkWusZvPIpxnRWoXaxJsFmVx0gJgjpJkYDyjaXUlp+jmaoseeZ4EPQUWqHLKJ59PRG0mg8j2xAjYH9nQaN14qMRmTGPxY8gfv/CUFcatA+8VJU9KEsJkDAwLVvglydNTLGrxpAJU78a2eaht0foV43XUIZGe3DKiJPgE+UOKGCJw==", + "type": "RsaSignature2017" + }, + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "type": "Create" +} diff --git a/test/fixtures/mewmew_no_name.json b/test/fixtures/mewmew_no_name.json new file mode 100644 index 000000000..532d4cf70 --- /dev/null +++ b/test/fixtures/mewmew_no_name.json @@ -0,0 +1,46 @@ +{ + "@context" : [ + "https://www.w3.org/ns/activitystreams", + "https://princess.cat/schemas/litepub-0.1.jsonld", + { + "@language" : "und" + } + ], + "attachment" : [], + "capabilities" : { + "acceptsChatMessages" : true + }, + "discoverable" : false, + "endpoints" : { + "oauthAuthorizationEndpoint" : "https://princess.cat/oauth/authorize", + "oauthRegistrationEndpoint" : "https://princess.cat/api/v1/apps", + "oauthTokenEndpoint" : "https://princess.cat/oauth/token", + "sharedInbox" : "https://princess.cat/inbox", + "uploadMedia" : "https://princess.cat/api/ap/upload_media" + }, + "followers" : "https://princess.cat/users/mewmew/followers", + "following" : "https://princess.cat/users/mewmew/following", + "icon" : { + "type" : "Image", + "url" : "https://princess.cat/media/12794fb50e86911e65be97f69196814049dcb398a2f8b58b99bb6591576e648c.png?name=blobcatpresentpink.png" + }, + "id" : "https://princess.cat/users/mewmew", + "image" : { + "type" : "Image", + "url" : "https://princess.cat/media/05d8bf3953ab6028fc920494ffc643fbee9dcef40d7bdd06f107e19acbfbd7f9.png" + }, + "inbox" : "https://princess.cat/users/mewmew/inbox", + "manuallyApprovesFollowers" : true, + "name" : " ", + "outbox" : "https://princess.cat/users/mewmew/outbox", + "preferredUsername" : "mewmew", + "publicKey" : { + "id" : "https://princess.cat/users/mewmew#main-key", + "owner" : "https://princess.cat/users/mewmew", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAru7VpygVef4zrFwnj0Mh\nrbO/2z2EdKN3rERtNrT8zWsLXNLQ50lfpRPnGDrd+xq7Rva4EIu0d5KJJ9n4vtY0\nuxK3On9vA2oyjLlR9O0lI3XTrHJborG3P7IPXrmNUMFpHiFHNqHp5tugUrs1gUFq\n7tmOmM92IP4Wjk8qNHFcsfnUbaPTX7sNIhteQKdi5HrTb/6lrEIe4G/FlMKRqxo3\nRNHuv6SNFQuiUKvFzjzazvjkjvBSm+aFROgdHa2tKl88StpLr7xmuY8qNFCRT6W0\nLacRp6c8ah5f03Kd+xCBVhCKvKaF1K0ERnQTBiitUh85md+Mtx/CoDoLnmpnngR3\nvQIDAQAB\n-----END PUBLIC KEY-----\n\n" + }, + "summary" : "please reply to my posts as direct messages if you have many followers", + "tag" : [], + "type" : "Person", + "url" : "https://princess.cat/users/mewmew" +} diff --git a/test/fixtures/modules/runtime_module.ex b/test/fixtures/modules/runtime_module.ex index f11032b57..e348c499e 100644 --- a/test/fixtures/modules/runtime_module.ex +++ b/test/fixtures/modules/runtime_module.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule RuntimeModule do +defmodule Fixtures.Modules.RuntimeModule do @moduledoc """ This is a dummy module to test custom runtime modules. """ diff --git a/test/instance_static/emoji/blobs.gg/blank.png b/test/instance_static/emoji/blobs.gg/blank.png new file mode 100644 index 000000000..8f50fa023 Binary files /dev/null and b/test/instance_static/emoji/blobs.gg/blank.png differ diff --git a/test/instance_static/emoji/blobs.gg/pack.json b/test/instance_static/emoji/blobs.gg/pack.json new file mode 100644 index 000000000..481891b08 --- /dev/null +++ b/test/instance_static/emoji/blobs.gg/pack.json @@ -0,0 +1,11 @@ +{ + "files": { + "blank": "blank.png" + }, + "pack": { + "description": "Test description", + "homepage": "https://pleroma.social", + "license": "Test license", + "share-files": true + } +} \ No newline at end of file diff --git a/test/tasks/pleroma_test.exs b/test/mix/pleroma_test.exs similarity index 100% rename from test/tasks/pleroma_test.exs rename to test/mix/pleroma_test.exs diff --git a/test/tasks/app_test.exs b/test/mix/tasks/pleroma/app_test.exs similarity index 100% rename from test/tasks/app_test.exs rename to test/mix/tasks/pleroma/app_test.exs diff --git a/test/tasks/config_test.exs b/test/mix/tasks/pleroma/config_test.exs similarity index 100% rename from test/tasks/config_test.exs rename to test/mix/tasks/pleroma/config_test.exs diff --git a/test/tasks/count_statuses_test.exs b/test/mix/tasks/pleroma/count_statuses_test.exs similarity index 100% rename from test/tasks/count_statuses_test.exs rename to test/mix/tasks/pleroma/count_statuses_test.exs diff --git a/test/tasks/database_test.exs b/test/mix/tasks/pleroma/database_test.exs similarity index 100% rename from test/tasks/database_test.exs rename to test/mix/tasks/pleroma/database_test.exs diff --git a/test/tasks/digest_test.exs b/test/mix/tasks/pleroma/digest_test.exs similarity index 89% rename from test/tasks/digest_test.exs rename to test/mix/tasks/pleroma/digest_test.exs index 0b444c86d..69dccb745 100644 --- a/test/tasks/digest_test.exs +++ b/test/mix/tasks/pleroma/digest_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Mix.Tasks.Pleroma.DigestTest do use Pleroma.DataCase diff --git a/test/tasks/ecto/migrate_test.exs b/test/mix/tasks/pleroma/ecto/migrate_test.exs similarity index 100% rename from test/tasks/ecto/migrate_test.exs rename to test/mix/tasks/pleroma/ecto/migrate_test.exs diff --git a/test/tasks/ecto/rollback_test.exs b/test/mix/tasks/pleroma/ecto/rollback_test.exs similarity index 100% rename from test/tasks/ecto/rollback_test.exs rename to test/mix/tasks/pleroma/ecto/rollback_test.exs diff --git a/test/tasks/ecto/ecto_test.exs b/test/mix/tasks/pleroma/ecto_test.exs similarity index 100% rename from test/tasks/ecto/ecto_test.exs rename to test/mix/tasks/pleroma/ecto_test.exs diff --git a/test/mix/tasks/pleroma/email_test.exs b/test/mix/tasks/pleroma/email_test.exs new file mode 100644 index 000000000..9523aefd8 --- /dev/null +++ b/test/mix/tasks/pleroma/email_test.exs @@ -0,0 +1,127 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.EmailTest do + use Pleroma.DataCase + + import Swoosh.TestAssertions + + alias Pleroma.Config + alias Pleroma.Tests.ObanHelpers + + import Pleroma.Factory + + setup_all do + Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(Mix.Shell.IO) + end) + + :ok + end + + setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true) + setup do: clear_config([:instance, :account_activation_required], true) + + describe "pleroma.email test" do + test "Sends test email with no given address" do + mail_to = Config.get([:instance, :email]) + + :ok = Mix.Tasks.Pleroma.Email.run(["test"]) + + ObanHelpers.perform_all() + + assert_receive {:mix_shell, :info, [message]} + assert message =~ "Test email has been sent" + + assert_email_sent( + to: mail_to, + html_body: ~r/a test email was requested./i + ) + end + + test "Sends test email with given address" do + mail_to = "hewwo@example.com" + + :ok = Mix.Tasks.Pleroma.Email.run(["test", "--to", mail_to]) + + ObanHelpers.perform_all() + + assert_receive {:mix_shell, :info, [message]} + assert message =~ "Test email has been sent" + + assert_email_sent( + to: mail_to, + html_body: ~r/a test email was requested./i + ) + end + + test "Sends confirmation emails" do + local_user1 = + insert(:user, %{ + confirmation_pending: true, + confirmation_token: "mytoken", + deactivated: false, + email: "local1@pleroma.com", + local: true + }) + + local_user2 = + insert(:user, %{ + confirmation_pending: true, + confirmation_token: "mytoken", + deactivated: false, + email: "local2@pleroma.com", + local: true + }) + + :ok = Mix.Tasks.Pleroma.Email.run(["resend_confirmation_emails"]) + + ObanHelpers.perform_all() + + assert_email_sent(to: {local_user1.name, local_user1.email}) + assert_email_sent(to: {local_user2.name, local_user2.email}) + end + + test "Does not send confirmation email to inappropriate users" do + # confirmed user + insert(:user, %{ + confirmation_pending: false, + confirmation_token: "mytoken", + deactivated: false, + email: "confirmed@pleroma.com", + local: true + }) + + # remote user + insert(:user, %{ + deactivated: false, + email: "remote@not-pleroma.com", + local: false + }) + + # deactivated user = + insert(:user, %{ + deactivated: true, + email: "deactivated@pleroma.com", + local: false + }) + + # invisible user + insert(:user, %{ + deactivated: false, + email: "invisible@pleroma.com", + local: true, + invisible: true + }) + + :ok = Mix.Tasks.Pleroma.Email.run(["resend_confirmation_emails"]) + + ObanHelpers.perform_all() + + refute_email_sent() + end + end +end diff --git a/test/tasks/emoji_test.exs b/test/mix/tasks/pleroma/emoji_test.exs similarity index 97% rename from test/tasks/emoji_test.exs rename to test/mix/tasks/pleroma/emoji_test.exs index 499f098c2..0fb8603ac 100644 --- a/test/tasks/emoji_test.exs +++ b/test/mix/tasks/pleroma/emoji_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Mix.Tasks.Pleroma.EmojiTest do use ExUnit.Case, async: true diff --git a/test/tasks/frontend_test.exs b/test/mix/tasks/pleroma/frontend_test.exs similarity index 98% rename from test/tasks/frontend_test.exs rename to test/mix/tasks/pleroma/frontend_test.exs index 022ae51be..6f9ec14cd 100644 --- a/test/tasks/frontend_test.exs +++ b/test/mix/tasks/pleroma/frontend_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.FrontendTest do +defmodule Mix.Tasks.Pleroma.FrontendTest do use Pleroma.DataCase alias Mix.Tasks.Pleroma.Frontend diff --git a/test/tasks/instance_test.exs b/test/mix/tasks/pleroma/instance_test.exs similarity index 82% rename from test/tasks/instance_test.exs rename to test/mix/tasks/pleroma/instance_test.exs index 3b4c041d9..fe69a2def 100644 --- a/test/tasks/instance_test.exs +++ b/test/mix/tasks/pleroma/instance_test.exs @@ -2,9 +2,11 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.InstanceTest do +defmodule Mix.Tasks.Pleroma.InstanceTest do use ExUnit.Case + @release_env_file "./test/pleroma.test.env" + setup do File.mkdir_p!(tmp_path()) @@ -16,6 +18,8 @@ defmodule Pleroma.InstanceTest do File.rm_rf(Path.join(static_dir, "robots.txt")) end + if File.exists?(@release_env_file), do: File.rm_rf(@release_env_file) + Pleroma.Config.put([:instance, :static_dir], static_dir) end) @@ -63,7 +67,15 @@ test "running gen" do "--uploads-dir", "test/uploads", "--static-dir", - "./test/../test/instance/static/" + "./test/../test/instance/static/", + "--strip-uploads", + "y", + "--dedupe-uploads", + "n", + "--anonymize-uploads", + "n", + "--release-env-file", + @release_env_file ]) end @@ -82,8 +94,12 @@ test "running gen" do assert generated_config =~ "password: \"dbpass\"" assert generated_config =~ "configurable_from_database: true" assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]" + assert generated_config =~ "filters: [Pleroma.Upload.Filter.ExifTool]" assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql() assert File.exists?(Path.expand("./test/instance/static/robots.txt")) + assert File.exists?(@release_env_file) + + assert File.read!(@release_env_file) =~ ~r/^RELEASE_COOKIE=.*/ end defp generated_setup_psql do diff --git a/test/tasks/refresh_counter_cache_test.exs b/test/mix/tasks/pleroma/refresh_counter_cache_test.exs similarity index 100% rename from test/tasks/refresh_counter_cache_test.exs rename to test/mix/tasks/pleroma/refresh_counter_cache_test.exs diff --git a/test/tasks/relay_test.exs b/test/mix/tasks/pleroma/relay_test.exs similarity index 57% rename from test/tasks/relay_test.exs rename to test/mix/tasks/pleroma/relay_test.exs index e5225b64c..cf48e7dda 100644 --- a/test/tasks/relay_test.exs +++ b/test/mix/tasks/pleroma/relay_test.exs @@ -81,6 +81,80 @@ test "relay is unfollowed" do assert undo_activity.data["object"]["id"] == cancelled_activity.data["id"] refute "#{target_instance}/followers" in User.following(local_user) end + + test "unfollow when relay is dead" do + user = insert(:user) + target_instance = user.ap_id + + Mix.Tasks.Pleroma.Relay.run(["follow", target_instance]) + + %User{ap_id: follower_id} = local_user = Relay.get_actor() + target_user = User.get_cached_by_ap_id(target_instance) + follow_activity = Utils.fetch_latest_follow(local_user, target_user) + User.follow(local_user, target_user) + + assert "#{target_instance}/followers" in User.following(local_user) + + Tesla.Mock.mock(fn %{method: :get, url: ^target_instance} -> + %Tesla.Env{status: 404} + end) + + Pleroma.Repo.delete(user) + Cachex.clear(:user_cache) + + Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance]) + + cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) + assert cancelled_activity.data["state"] == "accept" + + assert [] == + ActivityPub.fetch_activities( + [], + %{ + type: "Undo", + actor_id: follower_id, + skip_preload: true, + invisible_actors: true + } + ) + end + + test "force unfollow when relay is dead" do + user = insert(:user) + target_instance = user.ap_id + + Mix.Tasks.Pleroma.Relay.run(["follow", target_instance]) + + %User{ap_id: follower_id} = local_user = Relay.get_actor() + target_user = User.get_cached_by_ap_id(target_instance) + follow_activity = Utils.fetch_latest_follow(local_user, target_user) + User.follow(local_user, target_user) + + assert "#{target_instance}/followers" in User.following(local_user) + + Tesla.Mock.mock(fn %{method: :get, url: ^target_instance} -> + %Tesla.Env{status: 404} + end) + + Pleroma.Repo.delete(user) + Cachex.clear(:user_cache) + + Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance, "--force"]) + + cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) + assert cancelled_activity.data["state"] == "cancelled" + + [undo_activity] = + ActivityPub.fetch_activities( + [], + %{type: "Undo", actor_id: follower_id, skip_preload: true, invisible_actors: true} + ) + + assert undo_activity.data["type"] == "Undo" + assert undo_activity.data["actor"] == local_user.ap_id + assert undo_activity.data["object"]["id"] == cancelled_activity.data["id"] + refute "#{target_instance}/followers" in User.following(local_user) + end end describe "mix pleroma.relay list" do diff --git a/test/mix/tasks/pleroma/release_env_test.exs b/test/mix/tasks/pleroma/release_env_test.exs new file mode 100644 index 000000000..519f1eba9 --- /dev/null +++ b/test/mix/tasks/pleroma/release_env_test.exs @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.ReleaseEnvTest do + use ExUnit.Case + import ExUnit.CaptureIO, only: [capture_io: 1] + + @path "config/pleroma.test.env" + + def do_clean do + if File.exists?(@path) do + File.rm_rf(@path) + end + end + + setup do + do_clean() + on_exit(fn -> do_clean() end) + :ok + end + + test "generate pleroma.env" do + assert capture_io(fn -> + Mix.Tasks.Pleroma.ReleaseEnv.run(["gen", "--path", @path, "--force"]) + end) =~ "The file generated" + + assert File.read!(@path) =~ "RELEASE_COOKIE=" + end +end diff --git a/test/tasks/robots_txt_test.exs b/test/mix/tasks/pleroma/robots_txt_test.exs similarity index 100% rename from test/tasks/robots_txt_test.exs rename to test/mix/tasks/pleroma/robots_txt_test.exs diff --git a/test/tasks/uploads_test.exs b/test/mix/tasks/pleroma/uploads_test.exs similarity index 100% rename from test/tasks/uploads_test.exs rename to test/mix/tasks/pleroma/uploads_test.exs diff --git a/test/tasks/user_test.exs b/test/mix/tasks/pleroma/user_test.exs similarity index 88% rename from test/tasks/user_test.exs rename to test/mix/tasks/pleroma/user_test.exs index ce43a9cc7..ce819f815 100644 --- a/test/tasks/user_test.exs +++ b/test/mix/tasks/pleroma/user_test.exs @@ -225,47 +225,69 @@ test "no user to deactivate" do test "All statuses set" do user = insert(:user) - Mix.Tasks.Pleroma.User.run(["set", user.nickname, "--moderator", "--admin", "--locked"]) + Mix.Tasks.Pleroma.User.run([ + "set", + user.nickname, + "--admin", + "--confirmed", + "--locked", + "--moderator" + ]) assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Moderator status .* true/ + assert message =~ ~r/Admin status .* true/ + + assert_received {:mix_shell, :info, [message]} + assert message =~ ~r/Confirmation pending .* false/ assert_received {:mix_shell, :info, [message]} assert message =~ ~r/Locked status .* true/ assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Admin status .* true/ + assert message =~ ~r/Moderator status .* true/ user = User.get_cached_by_nickname(user.nickname) assert user.is_moderator - assert user.locked + assert user.is_locked assert user.is_admin + refute user.confirmation_pending end test "All statuses unset" do - user = insert(:user, locked: true, is_moderator: true, is_admin: true) + user = + insert(:user, + is_locked: true, + is_moderator: true, + is_admin: true, + confirmation_pending: true + ) Mix.Tasks.Pleroma.User.run([ "set", user.nickname, - "--no-moderator", "--no-admin", - "--no-locked" + "--no-confirmed", + "--no-locked", + "--no-moderator" ]) assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Moderator status .* false/ + assert message =~ ~r/Admin status .* false/ + + assert_received {:mix_shell, :info, [message]} + assert message =~ ~r/Confirmation pending .* true/ assert_received {:mix_shell, :info, [message]} assert message =~ ~r/Locked status .* false/ assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Admin status .* false/ + assert message =~ ~r/Moderator status .* false/ user = User.get_cached_by_nickname(user.nickname) refute user.is_moderator - refute user.locked + refute user.is_locked refute user.is_admin + assert user.confirmation_pending end test "no user to set status" do @@ -554,4 +576,44 @@ test "it prints an error message when user is not exist" do assert message =~ "Could not change user tags" end end + + describe "bulk confirm and unconfirm" do + test "confirm all" do + user1 = insert(:user, confirmation_pending: true) + user2 = insert(:user, confirmation_pending: true) + + assert user1.confirmation_pending + assert user2.confirmation_pending + + 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 + 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) + + refute user1.confirmation_pending + refute user2.confirmation_pending + + Mix.Tasks.Pleroma.User.run(["unconfirm_all"]) + + user1 = User.get_cached_by_nickname(user1.nickname) + user2 = User.get_cached_by_nickname(user2.nickname) + 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 + end + end end diff --git a/test/activity/ir/topics_test.exs b/test/pleroma/activity/ir/topics_test.exs similarity index 80% rename from test/activity/ir/topics_test.exs rename to test/pleroma/activity/ir/topics_test.exs index 14a6e6b71..5e5c2f8da 100644 --- a/test/activity/ir/topics_test.exs +++ b/test/pleroma/activity/ir/topics_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Activity.Ir.TopicsTest do use Pleroma.DataCase @@ -93,6 +97,20 @@ test "only converts strings to hash tags", %{ refute Enum.member?(topics, "hashtag:2") end + + test "non-local action produces public:remote topic", %{activity: activity} do + activity = %{activity | local: false, actor: "https://lain.com/users/lain"} + topics = Topics.get_activity_topics(activity) + + assert Enum.member?(topics, "public:remote:lain.com") + end + + test "local action doesn't produce public:remote topic", %{activity: activity} do + activity = %{activity | local: true, actor: "https://lain.com/users/lain"} + topics = Topics.get_activity_topics(activity) + + refute Enum.member?(topics, "public:remote:lain.com") + end end describe "public visibility create events with attachments" do @@ -124,6 +142,13 @@ test "non-local doesn't produce public:local:media topics", %{activity: activity refute Enum.member?(topics, "public:local:media") end + + test "non-local action produces public:remote:media topic", %{activity: activity} do + activity = %{activity | local: false, actor: "https://lain.com/users/lain"} + topics = Topics.get_activity_topics(activity) + + assert Enum.member?(topics, "public:remote:media:lain.com") + end end describe "non-public visibility" do diff --git a/test/activity_test.exs b/test/pleroma/activity_test.exs similarity index 100% rename from test/activity_test.exs rename to test/pleroma/activity_test.exs diff --git a/test/application_requirements_test.exs b/test/pleroma/application_requirements_test.exs similarity index 81% rename from test/application_requirements_test.exs rename to test/pleroma/application_requirements_test.exs index 21d24ddd0..c505ae229 100644 --- a/test/application_requirements_test.exs +++ b/test/pleroma/application_requirements_test.exs @@ -4,9 +4,12 @@ defmodule Pleroma.ApplicationRequirementsTest do use Pleroma.DataCase + import ExUnit.CaptureLog import Mock + alias Pleroma.ApplicationRequirements + alias Pleroma.Config alias Pleroma.Repo describe "check_welcome_message_config!/1" do @@ -70,42 +73,42 @@ 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 - Pleroma.Config.put([:database, :rum_enabled], true) + Config.put([:database, :rum_enabled], true) with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> false end]}]) do - assert_raise Pleroma.ApplicationRequirements.VerifyError, + assert_raise ApplicationRequirements.VerifyError, "Unapplied RUM Migrations detected", fn -> - capture_log(&Pleroma.ApplicationRequirements.verify!/0) + capture_log(&ApplicationRequirements.verify!/0) end end end test "raises if rum is disabled and detects rum migrations" do - Pleroma.Config.put([:database, :rum_enabled], false) + Config.put([:database, :rum_enabled], false) with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> true end]}]) do - assert_raise Pleroma.ApplicationRequirements.VerifyError, + assert_raise ApplicationRequirements.VerifyError, "RUM Migrations detected", fn -> - capture_log(&Pleroma.ApplicationRequirements.verify!/0) + capture_log(&ApplicationRequirements.verify!/0) end end end test "doesn't do anything if rum enabled and applied migrations" do - Pleroma.Config.put([:database, :rum_enabled], true) + Config.put([:database, :rum_enabled], true) with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> true end]}]) do - assert Pleroma.ApplicationRequirements.verify!() == :ok + assert ApplicationRequirements.verify!() == :ok end end test "doesn't do anything if rum disabled" do - Pleroma.Config.put([:database, :rum_enabled], false) + Config.put([:database, :rum_enabled], false) with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> false end]}]) do - assert Pleroma.ApplicationRequirements.verify!() == :ok + assert ApplicationRequirements.verify!() == :ok end end end @@ -130,17 +133,17 @@ test "doesn't do anything if rum disabled" do setup do: clear_config([:i_am_aware_this_may_cause_data_loss, :disable_migration_check]) test "raises if it detects unapplied migrations" do - assert_raise Pleroma.ApplicationRequirements.VerifyError, + assert_raise ApplicationRequirements.VerifyError, "Unapplied Migrations detected", fn -> - capture_log(&Pleroma.ApplicationRequirements.verify!/0) + capture_log(&ApplicationRequirements.verify!/0) end end test "doesn't do anything if disabled" do - Pleroma.Config.put([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true) + Config.put([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true) - assert :ok == Pleroma.ApplicationRequirements.verify!() + assert :ok == ApplicationRequirements.verify!() end end end diff --git a/test/bbs/handler_test.exs b/test/pleroma/bbs/handler_test.exs similarity index 100% rename from test/bbs/handler_test.exs rename to test/pleroma/bbs/handler_test.exs diff --git a/test/bookmark_test.exs b/test/pleroma/bookmark_test.exs similarity index 100% rename from test/bookmark_test.exs rename to test/pleroma/bookmark_test.exs diff --git a/test/captcha_test.exs b/test/pleroma/captcha_test.exs similarity index 100% rename from test/captcha_test.exs rename to test/pleroma/captcha_test.exs diff --git a/test/chat/message_reference_test.exs b/test/pleroma/chat/message_reference_test.exs similarity index 100% rename from test/chat/message_reference_test.exs rename to test/pleroma/chat/message_reference_test.exs diff --git a/test/chat_test.exs b/test/pleroma/chat_test.exs similarity index 100% rename from test/chat_test.exs rename to test/pleroma/chat_test.exs diff --git a/test/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs similarity index 74% rename from test/config/deprecation_warnings_test.exs rename to test/pleroma/config/deprecation_warnings_test.exs index e22052404..0cfed4555 100644 --- a/test/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -1,5 +1,9 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Config.DeprecationWarningsTest do - use ExUnit.Case, async: true + use ExUnit.Case use Pleroma.Tests.Helpers import ExUnit.CaptureLog @@ -66,6 +70,30 @@ test "check_media_proxy_whitelist_config/0" do end) =~ "Your config is using old format (only domain) for MediaProxy whitelist option" end + test "check_welcome_message_config/0" do + clear_config([:instance, :welcome_user_nickname], "LainChan") + + assert capture_log(fn -> + DeprecationWarnings.check_welcome_message_config() + end) =~ "Your config is using the old namespace for Welcome messages configuration." + end + + test "check_hellthread_threshold/0" do + clear_config([:mrf_hellthread, :threshold], 16) + + assert capture_log(fn -> + DeprecationWarnings.check_hellthread_threshold() + end) =~ "You are using the old configuration mechanism for the hellthread filter." + end + + test "check_activity_expiration_config/0" do + 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 + describe "check_gun_pool_options/0" do test "await_up_timeout" do config = Config.get(:connections_pool) @@ -74,7 +102,7 @@ test "await_up_timeout" do assert capture_log(fn -> DeprecationWarnings.check_gun_pool_options() end) =~ - "Your config is using old setting name `await_up_timeout` instead of `connect_timeout`" + "Your config is using old setting `config :pleroma, :connections_pool, await_up_timeout`." end test "pool timeout" do diff --git a/test/config/holder_test.exs b/test/pleroma/config/holder_test.exs similarity index 100% rename from test/config/holder_test.exs rename to test/pleroma/config/holder_test.exs diff --git a/test/config/loader_test.exs b/test/pleroma/config/loader_test.exs similarity index 100% rename from test/config/loader_test.exs rename to test/pleroma/config/loader_test.exs diff --git a/test/config/transfer_task_test.exs b/test/pleroma/config/transfer_task_test.exs similarity index 100% rename from test/config/transfer_task_test.exs rename to test/pleroma/config/transfer_task_test.exs diff --git a/test/config/config_db_test.exs b/test/pleroma/config_db_test.exs similarity index 100% rename from test/config/config_db_test.exs rename to test/pleroma/config_db_test.exs diff --git a/test/config_test.exs b/test/pleroma/config_test.exs similarity index 100% rename from test/config_test.exs rename to test/pleroma/config_test.exs diff --git a/test/conversation/participation_test.exs b/test/pleroma/conversation/participation_test.exs similarity index 90% rename from test/conversation/participation_test.exs rename to test/pleroma/conversation/participation_test.exs index 59a1b6492..5a603dcc1 100644 --- a/test/conversation/participation_test.exs +++ b/test/pleroma/conversation/participation_test.exs @@ -37,9 +37,8 @@ test "for a new conversation or a reply, it doesn't mark the author's participat [%{read: true}] = Participation.for_user(user) [%{read: false} = participation] = Participation.for_user(other_user) - - assert User.get_cached_by_id(user.id).unread_conversation_count == 0 - assert User.get_cached_by_id(other_user.id).unread_conversation_count == 1 + assert Participation.unread_count(user) == 0 + assert Participation.unread_count(other_user) == 1 {:ok, _} = CommonAPI.post(other_user, %{ @@ -54,8 +53,8 @@ test "for a new conversation or a reply, it doesn't mark the author's participat [%{read: false}] = Participation.for_user(user) [%{read: true}] = Participation.for_user(other_user) - assert User.get_cached_by_id(user.id).unread_conversation_count == 1 - assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0 + assert Participation.unread_count(user) == 1 + assert Participation.unread_count(other_user) == 0 end test "for a new conversation, it sets the recipents of the participation" do @@ -264,7 +263,7 @@ test "when the user blocks a recipient, the existing conversations with them are assert [%{read: false}, %{read: false}, %{read: false}, %{read: false}] = Participation.for_user(blocker) - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 4 + assert Participation.unread_count(blocker) == 4 {:ok, _user_relationship} = User.block(blocker, blocked) @@ -272,15 +271,15 @@ test "when the user blocks a recipient, the existing conversations with them are assert [%{read: true}, %{read: true}, %{read: true}, %{read: false}] = Participation.for_user(blocker) - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 1 + assert Participation.unread_count(blocker) == 1 # The conversation is not marked as read for the blocked user assert [_, _, %{read: false}] = Participation.for_user(blocked) - assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1 + assert Participation.unread_count(blocker) == 1 # The conversation is not marked as read for the third user assert [%{read: false}, _, _] = Participation.for_user(third_user) - assert User.get_cached_by_id(third_user.id).unread_conversation_count == 1 + assert Participation.unread_count(third_user) == 1 end test "the new conversation with the blocked user is not marked as unread " do @@ -298,7 +297,7 @@ test "the new conversation with the blocked user is not marked as unread " do }) assert [%{read: true}] = Participation.for_user(blocker) - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 + assert Participation.unread_count(blocker) == 0 # When the blocked user is a recipient {:ok, _direct2} = @@ -308,10 +307,10 @@ test "the new conversation with the blocked user is not marked as unread " do }) assert [%{read: true}, %{read: true}] = Participation.for_user(blocker) - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 + assert Participation.unread_count(blocker) == 0 assert [%{read: false}, _] = Participation.for_user(blocked) - assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1 + assert Participation.unread_count(blocked) == 1 end test "the conversation with the blocked user is not marked as unread on a reply" do @@ -327,8 +326,8 @@ test "the conversation with the blocked user is not marked as unread on a reply" {:ok, _user_relationship} = User.block(blocker, blocked) assert [%{read: true}] = Participation.for_user(blocker) - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 + assert Participation.unread_count(blocker) == 0 assert [blocked_participation] = Participation.for_user(blocked) # When it's a reply from the blocked user @@ -340,8 +339,8 @@ test "the conversation with the blocked user is not marked as unread on a reply" }) assert [%{read: true}] = Participation.for_user(blocker) - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 + assert Participation.unread_count(blocker) == 0 assert [third_user_participation] = Participation.for_user(third_user) # When it's a reply from the third user @@ -353,11 +352,12 @@ test "the conversation with the blocked user is not marked as unread on a reply" }) assert [%{read: true}] = Participation.for_user(blocker) - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 + assert Participation.unread_count(blocker) == 0 # Marked as unread for the blocked user assert [%{read: false}] = Participation.for_user(blocked) - assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1 + + assert Participation.unread_count(blocked) == 1 end end end diff --git a/test/conversation_test.exs b/test/pleroma/conversation_test.exs similarity index 100% rename from test/conversation_test.exs rename to test/pleroma/conversation_test.exs diff --git a/test/docs/generator_test.exs b/test/pleroma/docs/generator_test.exs similarity index 97% rename from test/docs/generator_test.exs rename to test/pleroma/docs/generator_test.exs index b32918a69..43877244d 100644 --- a/test/docs/generator_test.exs +++ b/test/pleroma/docs/generator_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Docs.GeneratorTest do use ExUnit.Case, async: true alias Pleroma.Docs.Generator diff --git a/test/earmark_renderer_test.exs b/test/pleroma/earmark_renderer_test.exs similarity index 100% rename from test/earmark_renderer_test.exs rename to test/pleroma/earmark_renderer_test.exs diff --git a/test/web/activity_pub/object_validators/types/date_time_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs similarity index 76% rename from test/web/activity_pub/object_validators/types/date_time_test.exs rename to test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs index 43be8e936..812463454 100644 --- a/test/web/activity_pub/object_validators/types/date_time_test.exs +++ b/test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs @@ -1,4 +1,8 @@ -defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTimeTest do +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 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 diff --git a/test/web/activity_pub/object_validators/types/object_id_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs similarity index 92% rename from test/web/activity_pub/object_validators/types/object_id_test.exs rename to test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs index e0ab76379..732e2365f 100644 --- a/test/web/activity_pub/object_validators/types/object_id_test.exs +++ b/test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.ObjectValidators.Types.ObjectIDTest do +defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectIDTest do alias Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID use Pleroma.DataCase diff --git a/test/web/activity_pub/object_validators/types/recipients_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs similarity index 78% rename from test/web/activity_pub/object_validators/types/recipients_test.exs rename to test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs index 053916bdd..2e6a0c83d 100644 --- a/test/web/activity_pub/object_validators/types/recipients_test.exs +++ b/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs @@ -1,4 +1,8 @@ -defmodule Pleroma.Web.ObjectValidators.Types.RecipientsTest do +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 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 diff --git a/test/web/activity_pub/object_validators/types/safe_text_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs similarity index 92% rename from test/web/activity_pub/object_validators/types/safe_text_test.exs rename to test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs index 9c08606f6..7eddd2388 100644 --- a/test/web/activity_pub/object_validators/types/safe_text_test.exs +++ b/test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.SafeTextTest do +defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.SafeTextTest do use Pleroma.DataCase alias Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText diff --git a/test/emails/admin_email_test.exs b/test/pleroma/emails/admin_email_test.exs similarity index 96% rename from test/emails/admin_email_test.exs rename to test/pleroma/emails/admin_email_test.exs index e24231e27..155057f3e 100644 --- a/test/emails/admin_email_test.exs +++ b/test/pleroma/emails/admin_email_test.exs @@ -63,7 +63,7 @@ test "new unapproved registration email" do assert res.html_body == """

New account for review: @#{account.nickname}

Plz let me in
- Visit AdminFE + Visit AdminFE """ end end diff --git a/test/emails/mailer_test.exs b/test/pleroma/emails/mailer_test.exs similarity index 100% rename from test/emails/mailer_test.exs rename to test/pleroma/emails/mailer_test.exs diff --git a/test/emails/user_email_test.exs b/test/pleroma/emails/user_email_test.exs similarity index 100% rename from test/emails/user_email_test.exs rename to test/pleroma/emails/user_email_test.exs diff --git a/test/emoji/formatter_test.exs b/test/pleroma/emoji/formatter_test.exs similarity index 100% rename from test/emoji/formatter_test.exs rename to test/pleroma/emoji/formatter_test.exs diff --git a/test/emoji/loader_test.exs b/test/pleroma/emoji/loader_test.exs similarity index 100% rename from test/emoji/loader_test.exs rename to test/pleroma/emoji/loader_test.exs diff --git a/test/emoji/pack_test.exs b/test/pleroma/emoji/pack_test.exs similarity index 100% rename from test/emoji/pack_test.exs rename to test/pleroma/emoji/pack_test.exs diff --git a/test/emoji_test.exs b/test/pleroma/emoji_test.exs similarity index 97% rename from test/emoji_test.exs rename to test/pleroma/emoji_test.exs index b36047578..1dd3c58c6 100644 --- a/test/emoji_test.exs +++ b/test/pleroma/emoji_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EmojiTest do - use ExUnit.Case, async: true + use ExUnit.Case alias Pleroma.Emoji describe "is_unicode_emoji?/1" do diff --git a/test/filter_test.exs b/test/pleroma/filter_test.exs similarity index 100% rename from test/filter_test.exs rename to test/pleroma/filter_test.exs diff --git a/test/following_relationship_test.exs b/test/pleroma/following_relationship_test.exs similarity index 100% rename from test/following_relationship_test.exs rename to test/pleroma/following_relationship_test.exs diff --git a/test/formatter_test.exs b/test/pleroma/formatter_test.exs similarity index 100% rename from test/formatter_test.exs rename to test/pleroma/formatter_test.exs diff --git a/test/gun/conneciton_pool_test.exs b/test/pleroma/gun/connection_pool_test.exs similarity index 100% rename from test/gun/conneciton_pool_test.exs rename to test/pleroma/gun/connection_pool_test.exs diff --git a/test/healthcheck_test.exs b/test/pleroma/healthcheck_test.exs similarity index 100% rename from test/healthcheck_test.exs rename to test/pleroma/healthcheck_test.exs diff --git a/test/html_test.exs b/test/pleroma/html_test.exs similarity index 100% rename from test/html_test.exs rename to test/pleroma/html_test.exs diff --git a/test/http/adapter_helper/gun_test.exs b/test/pleroma/http/adapter_helper/gun_test.exs similarity index 100% rename from test/http/adapter_helper/gun_test.exs rename to test/pleroma/http/adapter_helper/gun_test.exs diff --git a/test/http/adapter_helper/hackney_test.exs b/test/pleroma/http/adapter_helper/hackney_test.exs similarity index 100% rename from test/http/adapter_helper/hackney_test.exs rename to test/pleroma/http/adapter_helper/hackney_test.exs diff --git a/test/http/adapter_helper_test.exs b/test/pleroma/http/adapter_helper_test.exs similarity index 100% rename from test/http/adapter_helper_test.exs rename to test/pleroma/http/adapter_helper_test.exs diff --git a/test/http/ex_aws_test.exs b/test/pleroma/http/ex_aws_test.exs similarity index 100% rename from test/http/ex_aws_test.exs rename to test/pleroma/http/ex_aws_test.exs diff --git a/test/http/request_builder_test.exs b/test/pleroma/http/request_builder_test.exs similarity index 100% rename from test/http/request_builder_test.exs rename to test/pleroma/http/request_builder_test.exs diff --git a/test/http/tzdata_test.exs b/test/pleroma/http/tzdata_test.exs similarity index 100% rename from test/http/tzdata_test.exs rename to test/pleroma/http/tzdata_test.exs diff --git a/test/http_test.exs b/test/pleroma/http_test.exs similarity index 100% rename from test/http_test.exs rename to test/pleroma/http_test.exs diff --git a/test/web/instances/instance_test.exs b/test/pleroma/instances/instance_test.exs similarity index 100% rename from test/web/instances/instance_test.exs rename to test/pleroma/instances/instance_test.exs diff --git a/test/web/instances/instances_test.exs b/test/pleroma/instances_test.exs similarity index 100% rename from test/web/instances/instances_test.exs rename to test/pleroma/instances_test.exs diff --git a/test/federation/federation_test.exs b/test/pleroma/integration/federation_test.exs similarity index 100% rename from test/federation/federation_test.exs rename to test/pleroma/integration/federation_test.exs diff --git a/test/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs similarity index 97% rename from test/integration/mastodon_websocket_test.exs rename to test/pleroma/integration/mastodon_websocket_test.exs index 0f2e6cc2b..bb8e795b7 100644 --- a/test/integration/mastodon_websocket_test.exs +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -49,6 +49,7 @@ test "requires authentication and a valid token for protected streams" do test "allows public streams without authentication" do assert {:ok, _} = start_socket("?stream=public") assert {:ok, _} = start_socket("?stream=public:local") + assert {:ok, _} = start_socket("?stream=public:remote&instance=lain.com") assert {:ok, _} = start_socket("?stream=hashtag&tag=lain") end diff --git a/test/job_queue_monitor_test.exs b/test/pleroma/job_queue_monitor_test.exs similarity index 100% rename from test/job_queue_monitor_test.exs rename to test/pleroma/job_queue_monitor_test.exs diff --git a/test/keys_test.exs b/test/pleroma/keys_test.exs similarity index 100% rename from test/keys_test.exs rename to test/pleroma/keys_test.exs diff --git a/test/list_test.exs b/test/pleroma/list_test.exs similarity index 100% rename from test/list_test.exs rename to test/pleroma/list_test.exs diff --git a/test/marker_test.exs b/test/pleroma/marker_test.exs similarity index 100% rename from test/marker_test.exs rename to test/pleroma/marker_test.exs diff --git a/test/mfa/backup_codes_test.exs b/test/pleroma/mfa/backup_codes_test.exs similarity index 63% rename from test/mfa/backup_codes_test.exs rename to test/pleroma/mfa/backup_codes_test.exs index 7bc01b36b..41adb1e96 100644 --- a/test/mfa/backup_codes_test.exs +++ b/test/pleroma/mfa/backup_codes_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.MFA.BackupCodesTest do use Pleroma.DataCase diff --git a/test/mfa/totp_test.exs b/test/pleroma/mfa/totp_test.exs similarity index 72% rename from test/mfa/totp_test.exs rename to test/pleroma/mfa/totp_test.exs index 50153d208..9edb6fd54 100644 --- a/test/mfa/totp_test.exs +++ b/test/pleroma/mfa/totp_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.MFA.TOTPTest do use Pleroma.DataCase diff --git a/test/mfa_test.exs b/test/pleroma/mfa_test.exs similarity index 100% rename from test/mfa_test.exs rename to test/pleroma/mfa_test.exs diff --git a/test/migration_helper/notification_backfill_test.exs b/test/pleroma/migration_helper/notification_backfill_test.exs similarity index 100% rename from test/migration_helper/notification_backfill_test.exs rename to test/pleroma/migration_helper/notification_backfill_test.exs diff --git a/test/moderation_log_test.exs b/test/pleroma/moderation_log_test.exs similarity index 100% rename from test/moderation_log_test.exs rename to test/pleroma/moderation_log_test.exs diff --git a/test/notification_test.exs b/test/pleroma/notification_test.exs similarity index 99% rename from test/notification_test.exs rename to test/pleroma/notification_test.exs index b25239fd0..92c0bc8b6 100644 --- a/test/notification_test.exs +++ b/test/pleroma/notification_test.exs @@ -346,7 +346,7 @@ test "it creates notifications when someone likes user's status with a filtered describe "follow / follow_request notifications" do test "it creates `follow` notification for approved Follow activity" do user = insert(:user) - followed_user = insert(:user, locked: false) + followed_user = insert(:user, is_locked: false) {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) assert FollowingRelationship.following?(user, followed_user) @@ -361,7 +361,7 @@ test "it creates `follow` notification for approved Follow activity" do test "it creates `follow_request` notification for pending Follow activity" do user = insert(:user) - followed_user = insert(:user, locked: true) + followed_user = insert(:user, is_locked: true) {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) refute FollowingRelationship.following?(user, followed_user) @@ -383,7 +383,7 @@ test "it creates `follow_request` notification for pending Follow activity" do test "it doesn't create a notification for follow-unfollow-follow chains" do user = insert(:user) - followed_user = insert(:user, locked: false) + followed_user = insert(:user, is_locked: false) {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) assert FollowingRelationship.following?(user, followed_user) @@ -397,10 +397,10 @@ test "it doesn't create a notification for follow-unfollow-follow chains" do end test "dismisses the notification on follow request rejection" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) follower = insert(:user) {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user) - assert [notification] = Notification.for_user(user) + assert [_notification] = Notification.for_user(user) {:ok, _follower} = CommonAPI.reject_follow_request(follower, user) assert [] = Notification.for_user(user) end diff --git a/test/object/containment_test.exs b/test/pleroma/object/containment_test.exs similarity index 100% rename from test/object/containment_test.exs rename to test/pleroma/object/containment_test.exs diff --git a/test/object/fetcher_test.exs b/test/pleroma/object/fetcher_test.exs similarity index 100% rename from test/object/fetcher_test.exs rename to test/pleroma/object/fetcher_test.exs diff --git a/test/object_test.exs b/test/pleroma/object_test.exs similarity index 98% rename from test/object_test.exs rename to test/pleroma/object_test.exs index 198d3b1cf..99caba336 100644 --- a/test/object_test.exs +++ b/test/pleroma/object_test.exs @@ -82,7 +82,7 @@ test "Disabled via config" do Pleroma.Config.put([:instance, :cleanup_attachments], false) file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -116,7 +116,7 @@ test "in subdirectories" do Pleroma.Config.put([:instance, :cleanup_attachments], true) file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -155,7 +155,7 @@ test "with dedupe enabled" do File.mkdir_p!(uploads_dir) file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -188,7 +188,7 @@ test "with objects that have legacy data.url attribute" do Pleroma.Config.put([:instance, :cleanup_attachments], true) file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -225,7 +225,7 @@ test "With custom base_url" do Pleroma.Config.put([:instance, :cleanup_attachments], true) file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } diff --git a/test/otp_version_test.exs b/test/pleroma/otp_version_test.exs similarity index 100% rename from test/otp_version_test.exs rename to test/pleroma/otp_version_test.exs diff --git a/test/pagination_test.exs b/test/pleroma/pagination_test.exs similarity index 100% rename from test/pagination_test.exs rename to test/pleroma/pagination_test.exs diff --git a/test/registration_test.exs b/test/pleroma/registration_test.exs similarity index 100% rename from test/registration_test.exs rename to test/pleroma/registration_test.exs diff --git a/test/migrations/20200716195806_autolinker_to_linkify_test.exs b/test/pleroma/repo/migrations/autolinker_to_linkify_test.exs similarity index 93% rename from test/migrations/20200716195806_autolinker_to_linkify_test.exs rename to test/pleroma/repo/migrations/autolinker_to_linkify_test.exs index 250d11c61..84f520fe4 100644 --- a/test/migrations/20200716195806_autolinker_to_linkify_test.exs +++ b/test/pleroma/repo/migrations/autolinker_to_linkify_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Repo.Migrations.AutolinkerToLinkifyTest do use Pleroma.DataCase import Pleroma.Factory diff --git a/test/migrations/20200802170532_fix_legacy_tags_test.exs b/test/pleroma/repo/migrations/fix_legacy_tags_test.exs similarity index 83% rename from test/migrations/20200802170532_fix_legacy_tags_test.exs rename to test/pleroma/repo/migrations/fix_legacy_tags_test.exs index 3b4dee407..432055e45 100644 --- a/test/migrations/20200802170532_fix_legacy_tags_test.exs +++ b/test/pleroma/repo/migrations/fix_legacy_tags_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Repo.Migrations.FixLegacyTagsTest do alias Pleroma.User use Pleroma.DataCase diff --git a/test/migrations/20200722185515_fix_malformed_formatter_config_test.exs b/test/pleroma/repo/migrations/fix_malformed_formatter_config_test.exs similarity index 93% rename from test/migrations/20200722185515_fix_malformed_formatter_config_test.exs rename to test/pleroma/repo/migrations/fix_malformed_formatter_config_test.exs index d3490478e..61528599a 100644 --- a/test/migrations/20200722185515_fix_malformed_formatter_config_test.exs +++ b/test/pleroma/repo/migrations/fix_malformed_formatter_config_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Repo.Migrations.FixMalformedFormatterConfigTest do use Pleroma.DataCase import Pleroma.Factory diff --git a/test/migrations/20200724133313_move_welcome_settings_test.exs b/test/pleroma/repo/migrations/move_welcome_settings_test.exs similarity index 96% rename from test/migrations/20200724133313_move_welcome_settings_test.exs rename to test/pleroma/repo/migrations/move_welcome_settings_test.exs index 739f24547..53d05a55a 100644 --- a/test/migrations/20200724133313_move_welcome_settings_test.exs +++ b/test/pleroma/repo/migrations/move_welcome_settings_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Repo.Migrations.MoveWelcomeSettingsTest do use Pleroma.DataCase import Pleroma.Factory diff --git a/test/repo_test.exs b/test/pleroma/repo_test.exs similarity index 100% rename from test/repo_test.exs rename to test/pleroma/repo_test.exs diff --git a/test/report_note_test.exs b/test/pleroma/report_note_test.exs similarity index 100% rename from test/report_note_test.exs rename to test/pleroma/report_note_test.exs diff --git a/test/reverse_proxy/reverse_proxy_test.exs b/test/pleroma/reverse_proxy_test.exs similarity index 100% rename from test/reverse_proxy/reverse_proxy_test.exs rename to test/pleroma/reverse_proxy_test.exs diff --git a/test/runtime_test.exs b/test/pleroma/runtime_test.exs similarity index 69% rename from test/runtime_test.exs rename to test/pleroma/runtime_test.exs index a1a6c57cd..010594fcd 100644 --- a/test/runtime_test.exs +++ b/test/pleroma/runtime_test.exs @@ -6,6 +6,7 @@ defmodule Pleroma.RuntimeTest do use ExUnit.Case, async: true test "it loads custom runtime modules" do - assert {:module, RuntimeModule} == Code.ensure_compiled(RuntimeModule) + assert {:module, Fixtures.Modules.RuntimeModule} == + Code.ensure_compiled(Fixtures.Modules.RuntimeModule) end end diff --git a/test/safe_jsonb_set_test.exs b/test/pleroma/safe_jsonb_set_test.exs similarity index 71% rename from test/safe_jsonb_set_test.exs rename to test/pleroma/safe_jsonb_set_test.exs index 748540570..8b1274545 100644 --- a/test/safe_jsonb_set_test.exs +++ b/test/pleroma/safe_jsonb_set_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.SafeJsonbSetTest do use Pleroma.DataCase diff --git a/test/scheduled_activity_test.exs b/test/pleroma/scheduled_activity_test.exs similarity index 100% rename from test/scheduled_activity_test.exs rename to test/pleroma/scheduled_activity_test.exs diff --git a/test/signature_test.exs b/test/pleroma/signature_test.exs similarity index 100% rename from test/signature_test.exs rename to test/pleroma/signature_test.exs diff --git a/test/stats_test.exs b/test/pleroma/stats_test.exs similarity index 100% rename from test/stats_test.exs rename to test/pleroma/stats_test.exs diff --git a/test/upload/filter/anonymize_filename_test.exs b/test/pleroma/upload/filter/anonymize_filename_test.exs similarity index 97% rename from test/upload/filter/anonymize_filename_test.exs rename to test/pleroma/upload/filter/anonymize_filename_test.exs index 19b915cc8..7ef01ce91 100644 --- a/test/upload/filter/anonymize_filename_test.exs +++ b/test/pleroma/upload/filter/anonymize_filename_test.exs @@ -13,7 +13,7 @@ defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do upload_file = %Upload{ name: "an… image.jpg", - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg") } diff --git a/test/upload/filter/dedupe_test.exs b/test/pleroma/upload/filter/dedupe_test.exs similarity index 96% rename from test/upload/filter/dedupe_test.exs rename to test/pleroma/upload/filter/dedupe_test.exs index 75c7198e1..92a3d7df3 100644 --- a/test/upload/filter/dedupe_test.exs +++ b/test/pleroma/upload/filter/dedupe_test.exs @@ -18,7 +18,7 @@ test "adds shasum" do upload = %Upload{ name: "an… image.jpg", - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), tempfile: Path.absname("test/fixtures/image_tmp.jpg") } diff --git a/test/upload/filter/exiftool_test.exs b/test/pleroma/upload/filter/exiftool_test.exs similarity index 97% rename from test/upload/filter/exiftool_test.exs rename to test/pleroma/upload/filter/exiftool_test.exs index d4cd4ba11..6b978b64c 100644 --- a/test/upload/filter/exiftool_test.exs +++ b/test/pleroma/upload/filter/exiftool_test.exs @@ -16,7 +16,7 @@ test "apply exiftool filter" do upload = %Pleroma.Upload{ name: "image_with_GPS_data.jpg", - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/DSCN0010.jpg"), tempfile: Path.absname("test/fixtures/DSCN0010_tmp.jpg") } diff --git a/test/upload/filter/mogrifun_test.exs b/test/pleroma/upload/filter/mogrifun_test.exs similarity index 96% rename from test/upload/filter/mogrifun_test.exs rename to test/pleroma/upload/filter/mogrifun_test.exs index dc1e9e78f..fc2f68276 100644 --- a/test/upload/filter/mogrifun_test.exs +++ b/test/pleroma/upload/filter/mogrifun_test.exs @@ -17,7 +17,7 @@ test "apply mogrify filter" do upload = %Upload{ name: "an… image.jpg", - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), tempfile: Path.absname("test/fixtures/image_tmp.jpg") } diff --git a/test/upload/filter/mogrify_test.exs b/test/pleroma/upload/filter/mogrify_test.exs similarity index 96% rename from test/upload/filter/mogrify_test.exs rename to test/pleroma/upload/filter/mogrify_test.exs index bf64b96b3..6dee02e8b 100644 --- a/test/upload/filter/mogrify_test.exs +++ b/test/pleroma/upload/filter/mogrify_test.exs @@ -18,7 +18,7 @@ test "apply mogrify filter" do upload = %Pleroma.Upload{ name: "an… image.jpg", - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), tempfile: Path.absname("test/fixtures/image_tmp.jpg") } diff --git a/test/upload/filter_test.exs b/test/pleroma/upload/filter_test.exs similarity index 96% rename from test/upload/filter_test.exs rename to test/pleroma/upload/filter_test.exs index 352b66402..09394929c 100644 --- a/test/upload/filter_test.exs +++ b/test/pleroma/upload/filter_test.exs @@ -20,7 +20,7 @@ test "applies filters" do upload = %Pleroma.Upload{ name: "an… image.jpg", - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), tempfile: Path.absname("test/fixtures/image_tmp.jpg") } diff --git a/test/upload_test.exs b/test/pleroma/upload_test.exs similarity index 81% rename from test/upload_test.exs rename to test/pleroma/upload_test.exs index b06b54487..f52d4dff6 100644 --- a/test/upload_test.exs +++ b/test/pleroma/upload_test.exs @@ -11,7 +11,7 @@ defmodule Pleroma.UploadTest do alias Pleroma.Uploaders.Uploader @upload_file %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), filename: "image.jpg" } @@ -112,7 +112,7 @@ test "does not allow descriptions longer than the post limit" do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), filename: "image.jpg" } @@ -124,7 +124,7 @@ test "returns a media url" do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), filename: "image.jpg" } @@ -140,7 +140,7 @@ test "copies the file to the configured folder with deduping" do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), filename: "an [image.jpg" } @@ -156,7 +156,7 @@ test "copies the file to the configured folder without deduping" do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), filename: "an [image.jpg" } @@ -165,63 +165,31 @@ test "copies the file to the configured folder without deduping" do assert data["name"] == "an [image.jpg" end - test "fixes incorrect content type" do - File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") - - file = %Plug.Upload{ - content_type: "application/octet-stream", - path: Path.absname("test/fixtures/image_tmp.jpg"), - filename: "an [image.jpg" + test "fixes incorrect content type when base64 is given" do + params = %{ + img: "data:image/png;base64,#{Base.encode64(File.read!("test/fixtures/image.jpg"))}" } - {:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.Dedupe]) + {:ok, data} = Upload.store(params) assert hd(data["url"])["mediaType"] == "image/jpeg" end - test "adds missing extension" do + test "adds extension when base64 is given" do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image_tmp.jpg"), - filename: "an [image" + params = %{ + img: "data:image/png;base64,#{Base.encode64(File.read!("test/fixtures/image.jpg"))}" } - {:ok, data} = Upload.store(file) - assert data["name"] == "an [image.jpg" - end - - test "fixes incorrect file extension" do - File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") - - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image_tmp.jpg"), - filename: "an [image.blah" - } - - {:ok, data} = Upload.store(file) - assert data["name"] == "an [image.jpg" - end - - test "don't modify filename of an unknown type" do - File.cp("test/fixtures/test.txt", "test/fixtures/test_tmp.txt") - - file = %Plug.Upload{ - content_type: "text/plain", - path: Path.absname("test/fixtures/test_tmp.txt"), - filename: "test.txt" - } - - {:ok, data} = Upload.store(file) - assert data["name"] == "test.txt" + {:ok, data} = Upload.store(params) + assert String.ends_with?(data["name"], ".jpg") end test "copies the file to the configured folder with anonymizing filename" do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), filename: "an [image.jpg" } @@ -235,7 +203,7 @@ test "escapes invalid characters in url" do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), filename: "an… image.jpg" } @@ -250,7 +218,7 @@ test "escapes reserved uri characters" do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), filename: ":?#[]@!$&\\'()*+,;=.jpg" } @@ -272,7 +240,7 @@ test "returns a media url with configured base_url" do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), filename: "image.jpg" } diff --git a/test/uploaders/local_test.exs b/test/pleroma/uploaders/local_test.exs similarity index 95% rename from test/uploaders/local_test.exs rename to test/pleroma/uploaders/local_test.exs index 18122ff6c..1ce7be485 100644 --- a/test/uploaders/local_test.exs +++ b/test/pleroma/uploaders/local_test.exs @@ -19,7 +19,7 @@ test "put file to local folder" do file = %Pleroma.Upload{ name: "image.jpg", - content_type: "image/jpg", + content_type: "image/jpeg", path: file_path, tempfile: Path.absname("test/fixtures/image_tmp.jpg") } @@ -38,7 +38,7 @@ test "deletes local file" do file = %Pleroma.Upload{ name: "image.jpg", - content_type: "image/jpg", + content_type: "image/jpeg", path: file_path, tempfile: Path.absname("test/fixtures/image_tmp.jpg") } diff --git a/test/uploaders/s3_test.exs b/test/pleroma/uploaders/s3_test.exs similarity index 98% rename from test/uploaders/s3_test.exs rename to test/pleroma/uploaders/s3_test.exs index d949c90a5..e7a013dd8 100644 --- a/test/uploaders/s3_test.exs +++ b/test/pleroma/uploaders/s3_test.exs @@ -56,7 +56,7 @@ test "it returns path with bucket namespace when namespace is set" do setup do file_upload = %Pleroma.Upload{ name: "image-tet.jpg", - content_type: "image/jpg", + content_type: "image/jpeg", path: "test_folder/image-tet.jpg", tempfile: Path.absname("test/instance_static/add/shortcode.png") } diff --git a/test/pleroma/user/backup_test.exs b/test/pleroma/user/backup_test.exs new file mode 100644 index 000000000..f68e4a029 --- /dev/null +++ b/test/pleroma/user/backup_test.exs @@ -0,0 +1,244 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.BackupTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + import Mock + import Pleroma.Factory + import Swoosh.TestAssertions + + alias Pleroma.Bookmark + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User.Backup + alias Pleroma.Web.CommonAPI + alias Pleroma.Workers.BackupWorker + + setup do + clear_config([Pleroma.Upload, :uploader]) + clear_config([Backup, :limit_days]) + clear_config([Pleroma.Emails.Mailer, :enabled], true) + end + + test "it requries enabled email" do + Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false) + user = insert(:user) + assert {:error, "Backups require enabled email"} == Backup.create(user) + end + + test "it requries user's email" do + user = insert(:user, %{email: nil}) + assert {:error, "Email is required"} == Backup.create(user) + end + + test "it creates a backup record and an Oban job" do + %{id: user_id} = user = insert(:user) + assert {:ok, %Oban.Job{args: args}} = Backup.create(user) + assert_enqueued(worker: BackupWorker, args: args) + + backup = Backup.get(args["backup_id"]) + assert %Backup{user_id: ^user_id, processed: false, file_size: 0} = backup + end + + test "it return an error if the export limit is over" do + %{id: user_id} = user = insert(:user) + limit_days = Pleroma.Config.get([Backup, :limit_days]) + assert {:ok, %Oban.Job{args: args}} = Backup.create(user) + backup = Backup.get(args["backup_id"]) + assert %Backup{user_id: ^user_id, processed: false, file_size: 0} = backup + + assert Backup.create(user) == {:error, "Last export was less than #{limit_days} days ago"} + end + + test "it process a backup record" do + Pleroma.Config.put([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) + assert {:ok, backup} = perform_job(BackupWorker, args) + assert backup.file_size > 0 + assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id} = backup + + delete_job_args = %{"op" => "delete", "backup_id" => backup_id} + + assert_enqueued(worker: BackupWorker, args: delete_job_args) + assert {:ok, backup} = perform_job(BackupWorker, delete_job_args) + refute Backup.get(backup_id) + + email = Pleroma.Emails.UserEmail.backup_is_ready_email(backup) + + assert_email_sent( + to: {user.name, user.email}, + html_body: email.html_body + ) + 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) + user = insert(:user) + + assert {:ok, job1} = Backup.create(user) + + assert {:ok, %Backup{}} = ObanHelpers.perform(job1) + assert {:ok, job2} = Backup.create(user) + assert Pleroma.Repo.aggregate(Backup, :count) == 2 + assert {:ok, backup2} = ObanHelpers.perform(job2) + + ObanHelpers.perform_all() + + assert [^backup2] = Pleroma.Repo.all(Backup) + end + + test "it creates a zip archive with user data" do + user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"}) + + {:ok, %{object: %{data: %{"id" => id1}}} = status1} = + CommonAPI.post(user, %{status: "status1"}) + + {:ok, %{object: %{data: %{"id" => id2}}} = status2} = + CommonAPI.post(user, %{status: "status2"}) + + {:ok, %{object: %{data: %{"id" => id3}}} = status3} = + CommonAPI.post(user, %{status: "status3"}) + + CommonAPI.favorite(user, status1.id) + CommonAPI.favorite(user, status2.id) + + Bookmark.create(user.id, status2.id) + Bookmark.create(user.id, status3.id) + + assert {:ok, backup} = user |> Backup.new() |> Repo.insert() + assert {:ok, path} = Backup.export(backup) + assert {:ok, zipfile} = :zip.zip_open(String.to_charlist(path), [:memory]) + assert {:ok, {'actor.json', json}} = :zip.zip_get('actor.json', zipfile) + + assert %{ + "@context" => [ + "https://www.w3.org/ns/activitystreams", + "http://localhost:4001/schemas/litepub-0.1.jsonld", + %{"@language" => "und"} + ], + "bookmarks" => "bookmarks.json", + "followers" => "http://cofe.io/users/cofe/followers", + "following" => "http://cofe.io/users/cofe/following", + "id" => "http://cofe.io/users/cofe", + "inbox" => "http://cofe.io/users/cofe/inbox", + "likes" => "likes.json", + "name" => "Cofe", + "outbox" => "http://cofe.io/users/cofe/outbox", + "preferredUsername" => "cofe", + "publicKey" => %{ + "id" => "http://cofe.io/users/cofe#main-key", + "owner" => "http://cofe.io/users/cofe" + }, + "type" => "Person", + "url" => "http://cofe.io/users/cofe" + } = Jason.decode!(json) + + assert {:ok, {'outbox.json', json}} = :zip.zip_get('outbox.json', zipfile) + + assert %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "outbox.json", + "orderedItems" => [ + %{ + "object" => %{ + "actor" => "http://cofe.io/users/cofe", + "content" => "status1", + "type" => "Note" + }, + "type" => "Create" + }, + %{ + "object" => %{ + "actor" => "http://cofe.io/users/cofe", + "content" => "status2" + } + }, + %{ + "actor" => "http://cofe.io/users/cofe", + "object" => %{ + "content" => "status3" + } + } + ], + "totalItems" => 3, + "type" => "OrderedCollection" + } = Jason.decode!(json) + + assert {:ok, {'likes.json', json}} = :zip.zip_get('likes.json', zipfile) + + assert %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "likes.json", + "orderedItems" => [^id1, ^id2], + "totalItems" => 2, + "type" => "OrderedCollection" + } = Jason.decode!(json) + + assert {:ok, {'bookmarks.json', json}} = :zip.zip_get('bookmarks.json', zipfile) + + assert %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "bookmarks.json", + "orderedItems" => [^id2, ^id3], + "totalItems" => 2, + "type" => "OrderedCollection" + } = Jason.decode!(json) + + :zip.zip_close(zipfile) + File.rm!(path) + end + + 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]) + + user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"}) + + {:ok, status1} = CommonAPI.post(user, %{status: "status1"}) + {:ok, status2} = CommonAPI.post(user, %{status: "status2"}) + {:ok, status3} = CommonAPI.post(user, %{status: "status3"}) + CommonAPI.favorite(user, status1.id) + CommonAPI.favorite(user, status2.id) + Bookmark.create(user.id, status2.id) + Bookmark.create(user.id, status3.id) + + assert {:ok, backup} = user |> Backup.new() |> Repo.insert() + assert {:ok, path} = Backup.export(backup) + + [path: path, backup: backup] + end + + test "S3", %{path: path, backup: backup} do + Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.S3) + + with_mock ExAws, + request: fn + %{http_method: :put} -> {:ok, :ok} + %{http_method: :delete} -> {:ok, %{status_code: 204}} + end 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) + + assert {:ok, %Pleroma.Upload{}} = Backup.upload(backup, path) + assert {:ok, _backup} = Backup.delete(backup) + end + end +end diff --git a/test/user/import_test.exs b/test/pleroma/user/import_test.exs similarity index 100% rename from test/user/import_test.exs rename to test/pleroma/user/import_test.exs diff --git a/test/user/notification_setting_test.exs b/test/pleroma/user/notification_setting_test.exs similarity index 100% rename from test/user/notification_setting_test.exs rename to test/pleroma/user/notification_setting_test.exs diff --git a/test/pleroma/user/query_test.exs b/test/pleroma/user/query_test.exs new file mode 100644 index 000000000..e2f5c7d81 --- /dev/null +++ b/test/pleroma/user/query_test.exs @@ -0,0 +1,37 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.QueryTest do + use Pleroma.DataCase, async: true + + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.User.Query + alias Pleroma.Web.ActivityPub.InternalFetchActor + + import Pleroma.Factory + + describe "internal users" do + test "it filters out internal users by default" do + %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor() + + assert [_user] = User |> Repo.all() + assert [] == %{} |> Query.build() |> Repo.all() + end + + test "it filters out users without nickname by default" do + insert(:user, %{nickname: nil}) + + assert [_user] = User |> Repo.all() + assert [] == %{} |> Query.build() |> Repo.all() + end + + test "it returns internal users when enabled" do + %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor() + insert(:user, %{nickname: nil}) + + assert %{internal: true} |> Query.build() |> Repo.aggregate(:count) == 2 + end + end +end diff --git a/test/user/welcome_chat_massage_test.exs b/test/pleroma/user/welcome_chat_message_test.exs similarity index 100% rename from test/user/welcome_chat_massage_test.exs rename to test/pleroma/user/welcome_chat_message_test.exs diff --git a/test/user/welcome_email_test.exs b/test/pleroma/user/welcome_email_test.exs similarity index 100% rename from test/user/welcome_email_test.exs rename to test/pleroma/user/welcome_email_test.exs diff --git a/test/user/welcome_message_test.exs b/test/pleroma/user/welcome_message_test.exs similarity index 100% rename from test/user/welcome_message_test.exs rename to test/pleroma/user/welcome_message_test.exs diff --git a/test/user_invite_token_test.exs b/test/pleroma/user_invite_token_test.exs similarity index 100% rename from test/user_invite_token_test.exs rename to test/pleroma/user_invite_token_test.exs diff --git a/test/user_relationship_test.exs b/test/pleroma/user_relationship_test.exs similarity index 100% rename from test/user_relationship_test.exs rename to test/pleroma/user_relationship_test.exs diff --git a/test/user_search_test.exs b/test/pleroma/user_search_test.exs similarity index 87% rename from test/user_search_test.exs rename to test/pleroma/user_search_test.exs index 8529ce6db..31d787ffa 100644 --- a/test/user_search_test.exs +++ b/test/pleroma/user_search_test.exs @@ -17,6 +17,46 @@ defmodule Pleroma.UserSearchTest do describe "User.search" 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) + user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"}) + _user = insert(:user, %{nickname: "com_user"}) + + [first_user, _second_user] = User.search("https://lain.com/users/lain", resolve: true) + + assert first_user.id == user.id + end + + test "returns a user with matching ap_id as the first result" do + user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"}) + _user = insert(:user, %{nickname: "com_user"}) + + [first_user, _second_user] = User.search("https://lain.com/users/lain") + + assert first_user.id == user.id + end + + test "doesn't die if two users have the same uri" do + insert(:user, %{uri: "https://gensokyo.2hu/@raymoo"}) + insert(:user, %{uri: "https://gensokyo.2hu/@raymoo"}) + assert [_first_user, _second_user] = User.search("https://gensokyo.2hu/@raymoo") + end + + test "returns a user with matching uri as the first result" do + user = + insert(:user, %{ + nickname: "no_relation", + ap_id: "https://lain.com/users/lain", + uri: "https://lain.com/@lain" + }) + + _user = insert(:user, %{nickname: "com_user"}) + + [first_user, _second_user] = User.search("https://lain.com/@lain") + + assert first_user.id == user.id + end + test "excludes invisible users from results" do user = insert(:user, %{nickname: "john t1000"}) insert(:user, %{invisible: true, nickname: "john t800"}) @@ -26,7 +66,7 @@ test "excludes invisible users from results" do end test "excludes users when discoverable is false" do - insert(:user, %{nickname: "john 3000", discoverable: false}) + insert(:user, %{nickname: "john 3000", is_discoverable: false}) insert(:user, %{nickname: "john 3001"}) users = User.search("john") diff --git a/test/user_test.exs b/test/pleroma/user_test.exs similarity index 98% rename from test/user_test.exs rename to test/pleroma/user_test.exs index 00e1fcc6b..c678dadb3 100644 --- a/test/user_test.exs +++ b/test/pleroma/user_test.exs @@ -174,7 +174,7 @@ test "ap_following returns the following collection for the user" do test "returns all pending follow requests" do unlocked = insert(:user) - locked = insert(:user, locked: true) + locked = insert(:user, is_locked: true) follower = insert(:user) CommonAPI.follow(follower, unlocked) @@ -187,7 +187,7 @@ test "returns all pending follow requests" do end test "doesn't return already accepted or duplicate follow requests" do - locked = insert(:user, locked: true) + locked = insert(:user, is_locked: true) pending_follower = insert(:user) accepted_follower = insert(:user) @@ -201,7 +201,7 @@ test "doesn't return already accepted or duplicate follow requests" do end test "doesn't return follow requests for deactivated accounts" do - locked = insert(:user, locked: true) + locked = insert(:user, is_locked: true) pending_follower = insert(:user, %{deactivated: true}) CommonAPI.follow(pending_follower, locked) @@ -211,7 +211,7 @@ test "doesn't return follow requests for deactivated accounts" do end test "clears follow requests when requester is blocked" do - followed = insert(:user, locked: true) + followed = insert(:user, is_locked: true) follower = insert(:user) CommonAPI.follow(follower, followed) @@ -299,8 +299,8 @@ test "can't subscribe to a user who blocked us" do end test "local users do not automatically follow local locked accounts" do - follower = insert(:user, locked: true) - followed = insert(:user, locked: true) + follower = insert(:user, is_locked: true) + followed = insert(:user, is_locked: true) {:ok, follower} = User.maybe_direct_follow(follower, followed) @@ -388,6 +388,7 @@ test "fetches correct profile for nickname beginning with number" do } setup do: clear_config([:instance, :autofollowed_nicknames]) + setup do: clear_config([:instance, :autofollowing_nicknames]) setup do: clear_config([:welcome]) setup do: clear_config([:instance, :account_activation_required]) @@ -408,6 +409,23 @@ test "it autofollows accounts that are set for it" do refute User.following?(registered_user, remote_user) end + test "it adds automatic followers for new registered accounts" do + user1 = insert(:user) + user2 = insert(:user) + + Pleroma.Config.put([:instance, :autofollowing_nicknames], [ + user1.nickname, + user2.nickname + ]) + + cng = User.register_changeset(%User{}, @full_user_data) + + {:ok, registered_user} = User.register(cng) + + assert User.following?(user1, registered_user) + assert User.following?(user2, registered_user) + end + test "it sends a welcome message if it is set" do welcome_user = insert(:user) Pleroma.Config.put([:welcome, :direct_message, :enabled], true) @@ -1392,7 +1410,7 @@ test "it deactivates a user, all follow relationships and all activities", %{use follower = insert(:user) {:ok, follower} = User.follow(follower, user) - locked_user = insert(:user, name: "locked", locked: true) + locked_user = insert(:user, name: "locked", is_locked: true) {:ok, _} = User.follow(user, locked_user, :follow_pending) object = insert(:note, user: user) @@ -1482,7 +1500,7 @@ test "delete/1 purges a user when they wouldn't be fully deleted" do note_count: 9, follower_count: 9, following_count: 9001, - locked: true, + is_locked: true, confirmation_pending: true, password_reset_pending: true, approval_pending: true, @@ -1499,7 +1517,7 @@ test "delete/1 purges a user when they wouldn't be fully deleted" do pleroma_settings_store: %{"q" => "x"}, fields: [%{"gg" => "qq"}], raw_fields: [%{"gg" => "qq"}], - discoverable: true, + is_discoverable: true, also_known_as: ["https://lol.olo/users/loll"] }) @@ -1524,7 +1542,7 @@ test "delete/1 purges a user when they wouldn't be fully deleted" do note_count: 0, follower_count: 0, following_count: 0, - locked: false, + is_locked: false, confirmation_pending: false, password_reset_pending: false, approval_pending: false, @@ -1541,7 +1559,7 @@ test "delete/1 purges a user when they wouldn't be fully deleted" do pleroma_settings_store: %{}, fields: [], raw_fields: [], - discoverable: false, + is_discoverable: false, also_known_as: [] } = user end diff --git a/test/utils_test.exs b/test/pleroma/utils_test.exs similarity index 85% rename from test/utils_test.exs rename to test/pleroma/utils_test.exs index 3a730d545..460f7e0b5 100644 --- a/test/utils_test.exs +++ b/test/pleroma/utils_test.exs @@ -8,7 +8,7 @@ defmodule Pleroma.UtilsTest do describe "tmp_dir/1" do test "returns unique temporary directory" do {:ok, path} = Pleroma.Utils.tmp_dir("emoji") - assert path =~ ~r/\/tmp\/emoji-(.*)-#{:os.getpid()}-(.*)/ + assert path =~ ~r/\/emoji-(.*)-#{:os.getpid()}-(.*)/ File.rm_rf(path) end end diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs similarity index 96% rename from test/web/activity_pub/activity_pub_controller_test.exs rename to test/pleroma/web/activity_pub/activity_pub_controller_test.exs index 0517571f2..b696a24f4 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -156,21 +156,6 @@ test "it returns error when user is not found", %{conn: conn} do assert response == "Not found" end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - - conn = - put_req_header( - conn, - "accept", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ) - - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}.json", user) - end end describe "mastodon compatibility routes" do @@ -338,18 +323,6 @@ test "cached purged after object deletion", %{conn: conn} do assert "Not found" == json_response(conn2, :not_found) end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/objects/#{uuid}", user) - end end describe "/activities/:uuid" do @@ -421,18 +394,6 @@ test "cached purged after activity deletion", %{conn: conn} do assert "Not found" == json_response(conn2, :not_found) end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - activity = insert(:note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/activities/#{uuid}", user) - end end describe "/inbox" do @@ -893,15 +854,6 @@ test "it returns an announce activity in a collection", %{conn: conn} do assert response(conn, 200) =~ announce_activity.data["object"] end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}/outbox", user) - end end describe "POST /users/:nickname/outbox (C2S)" do @@ -1487,9 +1439,9 @@ test "POST /api/ap/upload_media", %{conn: conn} do desc = "Description of the image" image = %Plug.Upload{ - content_type: "image/jpg", + content_type: "bad/content-type", path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" + filename: "an_image.png" } object = @@ -1504,6 +1456,7 @@ test "POST /api/ap/upload_media", %{conn: conn} do assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"] assert is_binary(object_href) assert object_mediatype == "image/jpeg" + assert String.ends_with?(object_href, ".jpg") activity_request = %{ "@context" => "https://www.w3.org/ns/activitystreams", diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs similarity index 94% rename from test/web/activity_pub/activity_pub_test.exs rename to test/pleroma/web/activity_pub/activity_pub_test.exs index 7bdad3810..43bd14ee6 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -505,22 +505,22 @@ test "increases replies count", %{user: user} do # public {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "public")) - assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) assert object.data["repliesCount"] == 1 # unlisted {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "unlisted")) - assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) assert object.data["repliesCount"] == 2 # private {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "private")) - assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) assert object.data["repliesCount"] == 2 # direct {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "direct")) - assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) assert object.data["repliesCount"] == 2 end end @@ -752,6 +752,22 @@ test "does return activities from followed users on blocked domains" do refute repeat_activity in activities end + test "returns your own posts regardless of mute" do + user = insert(:user) + muted = insert(:user) + + {:ok, muted_post} = CommonAPI.post(muted, %{status: "Im stupid"}) + + {:ok, reply} = + CommonAPI.post(user, %{status: "I'm muting you", in_reply_to_status_id: muted_post.id}) + + {:ok, _} = User.mute(user, muted) + + [activity] = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true}) + + assert activity.id == reply.id + end + test "doesn't return muted activities" do activity_one = insert(:note_activity) activity_two = insert(:note_activity) @@ -1029,7 +1045,7 @@ test "returns reblogs for users for whom reblogs have not been muted" do describe "uploading files" do setup do test_file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -1120,7 +1136,7 @@ test "creates an undo activity for the last follow" do test "creates an undo activity for a pending follow request" do follower = insert(:user) - followed = insert(:user, %{locked: true}) + followed = insert(:user, %{is_locked: true}) {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) {:ok, activity} = ActivityPub.unfollow(follower, followed) @@ -2177,4 +2193,95 @@ test "does nothing with a clashing nickname and the same ap id" do assert user.nickname == orig_user.nickname end end + + describe "reply filtering" do + test "`following` still contains announcements by friends" do + user = insert(:user) + followed = insert(:user) + not_followed = insert(:user) + + User.follow(user, followed) + + {:ok, followed_post} = CommonAPI.post(followed, %{status: "Hello"}) + + {:ok, not_followed_to_followed} = + CommonAPI.post(not_followed, %{ + status: "Also hello", + in_reply_to_status_id: followed_post.id + }) + + {:ok, retoot} = CommonAPI.repeat(not_followed_to_followed.id, followed) + + params = + %{} + |> Map.put(:type, ["Create", "Announce"]) + |> Map.put(:blocking_user, user) + |> Map.put(:muting_user, user) + |> Map.put(:reply_filtering_user, user) + |> Map.put(:reply_visibility, "following") + |> Map.put(:announce_filtering_user, user) + |> Map.put(:user, user) + + activities = + [user.ap_id | User.following(user)] + |> ActivityPub.fetch_activities(params) + + followed_post_id = followed_post.id + retoot_id = retoot.id + + assert [%{id: ^followed_post_id}, %{id: ^retoot_id}] = activities + + assert length(activities) == 2 + end + + # This test is skipped because, while this is the desired behavior, + # there seems to be no good way to achieve it with the method that + # we currently use for detecting to who a reply is directed. + # This is a TODO and should be fixed by a later rewrite of the code + # in question. + @tag skip: true + test "`following` still contains self-replies by friends" do + user = insert(:user) + followed = insert(:user) + not_followed = insert(:user) + + User.follow(user, followed) + + {:ok, followed_post} = CommonAPI.post(followed, %{status: "Hello"}) + {:ok, not_followed_post} = CommonAPI.post(not_followed, %{status: "Also hello"}) + + {:ok, _followed_to_not_followed} = + CommonAPI.post(followed, %{status: "sup", in_reply_to_status_id: not_followed_post.id}) + + {:ok, _followed_self_reply} = + CommonAPI.post(followed, %{status: "Also cofe", in_reply_to_status_id: followed_post.id}) + + params = + %{} + |> Map.put(:type, ["Create", "Announce"]) + |> Map.put(:blocking_user, user) + |> Map.put(:muting_user, user) + |> Map.put(:reply_filtering_user, user) + |> Map.put(:reply_visibility, "following") + |> Map.put(:announce_filtering_user, user) + |> Map.put(:user, user) + + activities = + [user.ap_id | User.following(user)] + |> ActivityPub.fetch_activities(params) + + assert length(activities) == 2 + end + end + + test "allow fetching of accounts with an empty string name field" do + Tesla.Mock.mock(fn + %{method: :get, url: "https://princess.cat/users/mewmew"} -> + file = File.read!("test/fixtures/mewmew_no_name.json") + %Tesla.Env{status: 200, body: file} + end) + + {:ok, user} = ActivityPub.make_user_from_ap_id("https://princess.cat/users/mewmew") + assert user.name == " " + end end diff --git a/test/web/activity_pub/mrf/activity_expiration_policy_test.exs b/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/activity_expiration_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs diff --git a/test/web/activity_pub/mrf/anti_followbot_policy_test.exs b/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/anti_followbot_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs diff --git a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs b/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/anti_link_spam_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs diff --git a/test/web/activity_pub/mrf/ensure_re_prepended_test.exs b/test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs similarity index 100% rename from test/web/activity_pub/mrf/ensure_re_prepended_test.exs rename to test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs diff --git a/test/web/activity_pub/mrf/hellthread_policy_test.exs b/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/hellthread_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs diff --git a/test/web/activity_pub/mrf/keyword_policy_test.exs b/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/keyword_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs diff --git a/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs diff --git a/test/web/activity_pub/mrf/mention_policy_test.exs b/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/mention_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/mention_policy_test.exs diff --git a/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs b/test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs diff --git a/test/web/activity_pub/mrf/normalize_markup_test.exs b/test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs similarity index 100% rename from test/web/activity_pub/mrf/normalize_markup_test.exs rename to test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs diff --git a/test/web/activity_pub/mrf/object_age_policy_test.exs b/test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/object_age_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs diff --git a/test/web/activity_pub/mrf/reject_non_public_test.exs b/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs similarity index 92% rename from test/web/activity_pub/mrf/reject_non_public_test.exs rename to test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs index 58b46b9a2..e08eb3ba6 100644 --- a/test/web/activity_pub/mrf/reject_non_public_test.exs +++ b/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs @@ -21,7 +21,7 @@ test "it's allowed when address is public" do "type" => "Create" } - assert {:ok, message} = RejectNonPublic.filter(message) + assert {:ok, _message} = RejectNonPublic.filter(message) end test "it's allowed when cc address contain public address" do @@ -34,7 +34,7 @@ test "it's allowed when cc address contain public address" do "type" => "Create" } - assert {:ok, message} = RejectNonPublic.filter(message) + assert {:ok, _message} = RejectNonPublic.filter(message) end end @@ -50,7 +50,7 @@ test "it's allowed when addrer of message in the follower addresses of user and } Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], true) - assert {:ok, message} = RejectNonPublic.filter(message) + assert {:ok, _message} = RejectNonPublic.filter(message) end test "it's rejected when addrer of message in the follower addresses of user and it disabled in config" do @@ -80,7 +80,7 @@ test "it's allows when direct messages are allow" do } Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], true) - assert {:ok, message} = RejectNonPublic.filter(message) + assert {:ok, _message} = RejectNonPublic.filter(message) end test "it's reject when direct messages aren't allow" do diff --git a/test/web/activity_pub/mrf/simple_policy_test.exs b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/simple_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/simple_policy_test.exs diff --git a/test/web/activity_pub/mrf/steal_emoji_policy_test.exs b/test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/steal_emoji_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs diff --git a/test/web/activity_pub/mrf/subchain_policy_test.exs b/test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/subchain_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs diff --git a/test/web/activity_pub/mrf/tag_policy_test.exs b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs similarity index 98% rename from test/web/activity_pub/mrf/tag_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/tag_policy_test.exs index 6ff71d640..ffc30ba62 100644 --- a/test/web/activity_pub/mrf/tag_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs @@ -29,7 +29,7 @@ test "allows non-local follow requests" do actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"]) follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: true) message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id} - assert {:ok, message} = TagPolicy.filter(message) + assert {:ok, _message} = TagPolicy.filter(message) end end diff --git a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs b/test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/user_allowlist_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs diff --git a/test/web/activity_pub/mrf/vocabulary_policy_test.exs b/test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs similarity index 100% rename from test/web/activity_pub/mrf/vocabulary_policy_test.exs rename to test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs diff --git a/test/web/activity_pub/mrf/mrf_test.exs b/test/pleroma/web/activity_pub/mrf_test.exs similarity index 91% rename from test/web/activity_pub/mrf/mrf_test.exs rename to test/pleroma/web/activity_pub/mrf_test.exs index a63b25423..e8cdde2e1 100644 --- a/test/web/activity_pub/mrf/mrf_test.exs +++ b/test/pleroma/web/activity_pub/mrf_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRFTest do use ExUnit.Case, async: true use Pleroma.Tests.Helpers @@ -61,6 +65,8 @@ test "matches are case-insensitive" do describe "describe/0" do test "it works as expected with noop policy" do + clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoOpPolicy]) + expected = %{ mrf_policies: ["NoOpPolicy"], exclusions: false diff --git a/test/web/activity_pub/object_validators/accept_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/accept_validation_test.exs similarity index 100% rename from test/web/activity_pub/object_validators/accept_validation_test.exs rename to test/pleroma/web/activity_pub/object_validators/accept_validation_test.exs diff --git a/test/web/activity_pub/object_validators/announce_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/announce_validation_test.exs similarity index 97% rename from test/web/activity_pub/object_validators/announce_validation_test.exs rename to test/pleroma/web/activity_pub/object_validators/announce_validation_test.exs index 623342f76..4771c4698 100644 --- a/test/web/activity_pub/object_validators/announce_validation_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/announce_validation_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnouncValidationTest do +defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidationTest do use Pleroma.DataCase alias Pleroma.Object diff --git a/test/web/activity_pub/object_validators/article_note_validator_test.exs b/test/pleroma/web/activity_pub/object_validators/article_note_validator_test.exs similarity index 100% rename from test/web/activity_pub/object_validators/article_note_validator_test.exs rename to test/pleroma/web/activity_pub/object_validators/article_note_validator_test.exs diff --git a/test/web/activity_pub/object_validators/attachment_validator_test.exs b/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs similarity index 98% rename from test/web/activity_pub/object_validators/attachment_validator_test.exs rename to test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs index 558bb3131..760388e80 100644 --- a/test/web/activity_pub/object_validators/attachment_validator_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs @@ -56,7 +56,7 @@ test "it handles our own uploads" do user = insert(:user) file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } diff --git a/test/web/activity_pub/object_validators/block_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/block_validation_test.exs similarity index 100% rename from test/web/activity_pub/object_validators/block_validation_test.exs rename to test/pleroma/web/activity_pub/object_validators/block_validation_test.exs diff --git a/test/web/activity_pub/object_validators/chat_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs similarity index 98% rename from test/web/activity_pub/object_validators/chat_validation_test.exs rename to test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs index 16e4808e5..d7e299224 100644 --- a/test/web/activity_pub/object_validators/chat_validation_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs @@ -77,7 +77,7 @@ test "validates for a basic object with an attachment", %{ user: user } do file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -98,7 +98,7 @@ test "validates for a basic object with an attachment in an array", %{ user: user } do file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -119,7 +119,7 @@ test "validates for a basic object with an attachment but without content", %{ user: user } do file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } diff --git a/test/web/activity_pub/object_validators/delete_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/delete_validation_test.exs similarity index 100% rename from test/web/activity_pub/object_validators/delete_validation_test.exs rename to test/pleroma/web/activity_pub/object_validators/delete_validation_test.exs diff --git a/test/web/activity_pub/object_validators/emoji_react_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs similarity index 100% rename from test/web/activity_pub/object_validators/emoji_react_validation_test.exs rename to test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs diff --git a/test/web/activity_pub/object_validators/follow_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs similarity index 100% rename from test/web/activity_pub/object_validators/follow_validation_test.exs rename to test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs diff --git a/test/web/activity_pub/object_validators/like_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/like_validation_test.exs similarity index 100% rename from test/web/activity_pub/object_validators/like_validation_test.exs rename to test/pleroma/web/activity_pub/object_validators/like_validation_test.exs diff --git a/test/web/activity_pub/object_validators/reject_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/reject_validation_test.exs similarity index 100% rename from test/web/activity_pub/object_validators/reject_validation_test.exs rename to test/pleroma/web/activity_pub/object_validators/reject_validation_test.exs diff --git a/test/web/activity_pub/object_validators/undo_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/undo_handling_test.exs similarity index 100% rename from test/web/activity_pub/object_validators/undo_validation_test.exs rename to test/pleroma/web/activity_pub/object_validators/undo_handling_test.exs diff --git a/test/web/activity_pub/object_validators/update_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/update_handling_test.exs similarity index 100% rename from test/web/activity_pub/object_validators/update_validation_test.exs rename to test/pleroma/web/activity_pub/object_validators/update_handling_test.exs diff --git a/test/web/activity_pub/pipeline_test.exs b/test/pleroma/web/activity_pub/pipeline_test.exs similarity index 100% rename from test/web/activity_pub/pipeline_test.exs rename to test/pleroma/web/activity_pub/pipeline_test.exs diff --git a/test/web/activity_pub/publisher_test.exs b/test/pleroma/web/activity_pub/publisher_test.exs similarity index 100% rename from test/web/activity_pub/publisher_test.exs rename to test/pleroma/web/activity_pub/publisher_test.exs diff --git a/test/web/activity_pub/relay_test.exs b/test/pleroma/web/activity_pub/relay_test.exs similarity index 76% rename from test/web/activity_pub/relay_test.exs rename to test/pleroma/web/activity_pub/relay_test.exs index 9d657ac4f..3284980f7 100644 --- a/test/web/activity_pub/relay_test.exs +++ b/test/pleroma/web/activity_pub/relay_test.exs @@ -63,6 +63,46 @@ test "returns activity" do assert activity.data["to"] == [user.ap_id] refute "#{user.ap_id}/followers" in User.following(service_actor) end + + test "force unfollow when target service is dead" do + user = insert(:user) + user_ap_id = user.ap_id + user_id = user.id + + Tesla.Mock.mock(fn %{method: :get, url: ^user_ap_id} -> + %Tesla.Env{status: 404} + end) + + service_actor = Relay.get_actor() + CommonAPI.follow(service_actor, user) + assert "#{user.ap_id}/followers" in User.following(service_actor) + + assert Pleroma.Repo.get_by( + Pleroma.FollowingRelationship, + follower_id: service_actor.id, + following_id: user_id + ) + + Pleroma.Repo.delete(user) + Cachex.clear(:user_cache) + + assert {:ok, %Activity{} = activity} = Relay.unfollow(user_ap_id, %{force: true}) + + assert refresh_record(service_actor).following_count == 0 + + refute Pleroma.Repo.get_by( + Pleroma.FollowingRelationship, + follower_id: service_actor.id, + following_id: user_id + ) + + assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" + assert user.ap_id in activity.recipients + assert activity.data["type"] == "Undo" + assert activity.data["actor"] == service_actor.ap_id + assert activity.data["to"] == [user_ap_id] + refute "#{user.ap_id}/followers" in User.following(service_actor) + end end describe "publish/1" do diff --git a/test/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs similarity index 100% rename from test/web/activity_pub/side_effects_test.exs rename to test/pleroma/web/activity_pub/side_effects_test.exs diff --git a/test/web/activity_pub/transmogrifier/accept_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/accept_handling_test.exs similarity index 96% rename from test/web/activity_pub/transmogrifier/accept_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/accept_handling_test.exs index 77d468f5c..c6ff96f08 100644 --- a/test/web/activity_pub/transmogrifier/accept_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/accept_handling_test.exs @@ -46,7 +46,7 @@ test "it works for incoming accepts which were pre-accepted" do test "it works for incoming accepts which are referenced by IRI only" do follower = insert(:user) - followed = insert(:user, locked: true) + followed = insert(:user, is_locked: true) {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) @@ -72,7 +72,7 @@ test "it works for incoming accepts which are referenced by IRI only" do test "it fails for incoming accepts which cannot be correlated" do follower = insert(:user) - followed = insert(:user, locked: true) + followed = insert(:user, is_locked: true) accept_data = File.read!("test/fixtures/mastodon-accept-activity.json") diff --git a/test/web/activity_pub/transmogrifier/announce_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs similarity index 98% rename from test/web/activity_pub/transmogrifier/announce_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs index e895636b5..54335acdb 100644 --- a/test/web/activity_pub/transmogrifier/announce_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs @@ -144,7 +144,7 @@ test "it rejects incoming announces with an inlined activity from another origin _user = insert(:user, local: false, ap_id: data["actor"]) - assert {:error, e} = Transmogrifier.handle_incoming(data) + assert {:error, _e} = Transmogrifier.handle_incoming(data) end test "it does not clobber the addressing on announce activities" do diff --git a/test/web/activity_pub/transmogrifier/answer_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/answer_handling_test.exs similarity index 96% rename from test/web/activity_pub/transmogrifier/answer_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/answer_handling_test.exs index 0f6605c3f..e7d85a2c5 100644 --- a/test/web/activity_pub/transmogrifier/answer_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/answer_handling_test.exs @@ -27,6 +27,7 @@ test "incoming, rewrites Note to Answer and increments vote counters" do }) object = Object.normalize(activity) + assert object.data["repliesCount"] == nil data = File.read!("test/fixtures/mastodon-vote.json") @@ -41,7 +42,7 @@ test "incoming, rewrites Note to Answer and increments vote counters" do assert answer_object.data["inReplyTo"] == object.data["id"] new_object = Object.get_by_ap_id(object.data["id"]) - assert new_object.data["replies_count"] == object.data["replies_count"] + assert new_object.data["repliesCount"] == nil assert Enum.any?( new_object.data["oneOf"], diff --git a/test/web/activity_pub/transmogrifier/article_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs similarity index 100% rename from test/web/activity_pub/transmogrifier/article_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs diff --git a/test/web/activity_pub/transmogrifier/audio_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs similarity index 100% rename from test/web/activity_pub/transmogrifier/audio_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs diff --git a/test/web/activity_pub/transmogrifier/block_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/block_handling_test.exs similarity index 100% rename from test/web/activity_pub/transmogrifier/block_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/block_handling_test.exs diff --git a/test/web/activity_pub/transmogrifier/chat_message_test.exs b/test/pleroma/web/activity_pub/transmogrifier/chat_message_test.exs similarity index 100% rename from test/web/activity_pub/transmogrifier/chat_message_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/chat_message_test.exs diff --git a/test/web/activity_pub/transmogrifier/delete_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/delete_handling_test.exs similarity index 100% rename from test/web/activity_pub/transmogrifier/delete_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/delete_handling_test.exs diff --git a/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs similarity index 100% rename from test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs diff --git a/test/web/activity_pub/transmogrifier/event_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs similarity index 100% rename from test/web/activity_pub/transmogrifier/event_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs diff --git a/test/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/follow_handling_test.exs similarity index 98% rename from test/web/activity_pub/transmogrifier/follow_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/follow_handling_test.exs index 757d90941..4ef8210ad 100644 --- a/test/web/activity_pub/transmogrifier/follow_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/follow_handling_test.exs @@ -65,7 +65,7 @@ test "it works for incoming follow requests" do end test "with locked accounts, it does create a Follow, but not an Accept" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) data = File.read!("test/fixtures/mastodon-follow-activity.json") @@ -188,7 +188,7 @@ test "it works for incoming follow requests from hubzilla" do test "it works for incoming follows to locked account" do pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin") - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) data = File.read!("test/fixtures/mastodon-follow-activity.json") diff --git a/test/web/activity_pub/transmogrifier/like_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/like_handling_test.exs similarity index 100% rename from test/web/activity_pub/transmogrifier/like_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/like_handling_test.exs diff --git a/test/web/activity_pub/transmogrifier/question_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/question_handling_test.exs similarity index 97% rename from test/web/activity_pub/transmogrifier/question_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/question_handling_test.exs index 74ee79543..d2822ce75 100644 --- a/test/web/activity_pub/transmogrifier/question_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/question_handling_test.exs @@ -157,12 +157,12 @@ test "Mastodon Question activity with custom emojis" do } end - test "returns an error if received a second time" do + test "returns same activity if received a second time" do data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() assert {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - assert {:error, {:validate_object, {:error, _}}} = Transmogrifier.handle_incoming(data) + assert {:ok, ^activity} = Transmogrifier.handle_incoming(data) end test "accepts a Question with no content" do diff --git a/test/web/activity_pub/transmogrifier/reject_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/reject_handling_test.exs similarity index 95% rename from test/web/activity_pub/transmogrifier/reject_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/reject_handling_test.exs index 7592fbe1c..5c1451def 100644 --- a/test/web/activity_pub/transmogrifier/reject_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/reject_handling_test.exs @@ -14,7 +14,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.RejectHandlingTest do test "it fails for incoming rejects which cannot be correlated" do follower = insert(:user) - followed = insert(:user, locked: true) + followed = insert(:user, is_locked: true) accept_data = File.read!("test/fixtures/mastodon-reject-activity.json") @@ -33,7 +33,7 @@ test "it fails for incoming rejects which cannot be correlated" do test "it works for incoming rejects which are referenced by IRI only" do follower = insert(:user) - followed = insert(:user, locked: true) + followed = insert(:user, is_locked: true) {:ok, follower} = User.follow(follower, followed) {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) diff --git a/test/web/activity_pub/transmogrifier/undo_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs similarity index 100% rename from test/web/activity_pub/transmogrifier/undo_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs diff --git a/test/web/activity_pub/transmogrifier/user_update_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs similarity index 99% rename from test/web/activity_pub/transmogrifier/user_update_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs index 64636656c..7c4d16db7 100644 --- a/test/web/activity_pub/transmogrifier/user_update_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs @@ -154,6 +154,6 @@ test "it works for incoming update activities which lock the account" do {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(update_data) user = User.get_cached_by_ap_id(user.ap_id) - assert user.locked == true + assert user.is_locked == true end end diff --git a/test/web/activity_pub/transmogrifier/video_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/video_handling_test.exs similarity index 100% rename from test/web/activity_pub/transmogrifier/video_handling_test.exs rename to test/pleroma/web/activity_pub/transmogrifier/video_handling_test.exs diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs similarity index 98% rename from test/web/activity_pub/transmogrifier_test.exs rename to test/pleroma/web/activity_pub/transmogrifier_test.exs index 561674f01..e39af1dfc 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -101,7 +101,7 @@ test "it fetches reply-to activities if we don't have them" do {:ok, returned_activity} = Transmogrifier.handle_incoming(data) returned_object = Object.normalize(returned_activity, false) - assert activity = + assert %Activity{} = Activity.get_create_by_object_ap_id( "https://mstdn.io/users/mayuutann/statuses/99568293732299394" ) @@ -206,6 +206,16 @@ test "it works for incoming notices" do assert user.note_count == 1 end + test "it works for incoming notices without the sensitive property but an nsfw hashtag" do + data = File.read!("test/fixtures/mastodon-post-activity-nsfw.json") |> Poison.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + object_data = Object.normalize(data["object"], false).data + + assert object_data["sensitive"] == true + end + test "it works for incoming notices with hashtags" do data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!() diff --git a/test/web/activity_pub/utils_test.exs b/test/pleroma/web/activity_pub/utils_test.exs similarity index 99% rename from test/web/activity_pub/utils_test.exs rename to test/pleroma/web/activity_pub/utils_test.exs index d50213545..be9cd7d13 100644 --- a/test/web/activity_pub/utils_test.exs +++ b/test/pleroma/web/activity_pub/utils_test.exs @@ -193,7 +193,7 @@ test "fetches only Create activities" do describe "update_follow_state_for_all/2" do test "updates the state of all Follow activities with the same actor and object" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) follower = insert(:user) {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) @@ -217,7 +217,7 @@ test "updates the state of all Follow activities with the same actor and object" describe "update_follow_state/2" do test "updates the state of the given follow activity" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) follower = insert(:user) {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) diff --git a/test/web/activity_pub/views/object_view_test.exs b/test/pleroma/web/activity_pub/views/object_view_test.exs similarity index 100% rename from test/web/activity_pub/views/object_view_test.exs rename to test/pleroma/web/activity_pub/views/object_view_test.exs diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/pleroma/web/activity_pub/views/user_view_test.exs similarity index 100% rename from test/web/activity_pub/views/user_view_test.exs rename to test/pleroma/web/activity_pub/views/user_view_test.exs diff --git a/test/web/activity_pub/visibilty_test.exs b/test/pleroma/web/activity_pub/visibility_test.exs similarity index 100% rename from test/web/activity_pub/visibilty_test.exs rename to test/pleroma/web/activity_pub/visibility_test.exs 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 new file mode 100644 index 000000000..c06ae55ca --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs @@ -0,0 +1,1054 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do + use Pleroma.Web.ConnCase + use Oban.Testing, repo: Pleroma.Repo + + import ExUnit.CaptureLog + import Pleroma.Factory + import Swoosh.TestAssertions + + alias Pleroma.Activity + alias Pleroma.MFA + alias Pleroma.ModerationLog + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + + :ok + end + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + test "with valid `admin_token` query parameter, skips OAuth scopes check" do + clear_config([:admin_token], "password123") + + user = insert(:user) + + conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123") + + 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}" + + 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 + + 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 + end + end + + describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do + setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) + + 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}" + + 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"]) + + 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 + end + end + + describe "PUT /api/pleroma/admin/users/tag" do + setup %{conn: conn} do + user1 = insert(:user, %{tags: ["x"]}) + user2 = insert(:user, %{tags: ["y"]}) + user3 = insert(:user, %{tags: ["unchanged"]}) + + conn = + conn + |> put_req_header("accept", "application/json") + |> put( + "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <> + "#{user2.nickname}&tags[]=foo&tags[]=bar" + ) + + %{conn: conn, user1: user1, user2: user2, user3: user3} + end + + test "it appends specified tags to users with specified nicknames", %{ + conn: conn, + admin: admin, + user1: user1, + user2: user2 + } do + assert empty_json_response(conn) + assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"] + assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"] + + log_entry = Repo.one(ModerationLog) + + users = + [user1.nickname, user2.nickname] + |> Enum.map(&"@#{&1}") + |> Enum.join(", ") + + tags = ["foo", "bar"] |> Enum.join(", ") + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} added tags: #{tags} to users: #{users}" + end + + test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do + assert empty_json_response(conn) + assert User.get_cached_by_id(user3.id).tags == ["unchanged"] + end + end + + describe "DELETE /api/pleroma/admin/users/tag" do + setup %{conn: conn} do + user1 = insert(:user, %{tags: ["x"]}) + user2 = insert(:user, %{tags: ["y", "z"]}) + user3 = insert(:user, %{tags: ["unchanged"]}) + + conn = + conn + |> put_req_header("accept", "application/json") + |> delete( + "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <> + "#{user2.nickname}&tags[]=x&tags[]=z" + ) + + %{conn: conn, user1: user1, user2: user2, user3: user3} + end + + test "it removes specified tags from users with specified nicknames", %{ + conn: conn, + admin: admin, + user1: user1, + user2: user2 + } do + assert empty_json_response(conn) + assert User.get_cached_by_id(user1.id).tags == [] + assert User.get_cached_by_id(user2.id).tags == ["y"] + + log_entry = Repo.one(ModerationLog) + + users = + [user1.nickname, user2.nickname] + |> Enum.map(&"@#{&1}") + |> Enum.join(", ") + + tags = ["x", "z"] |> Enum.join(", ") + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} removed tags: #{tags} from users: #{users}" + end + + test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do + assert empty_json_response(conn) + assert User.get_cached_by_id(user3.id).tags == ["unchanged"] + end + end + + describe "/api/pleroma/admin/users/:nickname/permission_group" do + test "GET is giving user_info", %{admin: admin, conn: conn} do + conn = + conn + |> put_req_header("accept", "application/json") + |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/") + + assert json_response(conn, 200) == %{ + "is_admin" => true, + "is_moderator" => false + } + end + + test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin") + + assert json_response(conn, 200) == %{ + "is_admin" => true + } + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} made @#{user.nickname} admin" + end + + test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do + user_one = insert(:user) + user_two = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users/permission_group/admin", %{ + nicknames: [user_one.nickname, user_two.nickname] + }) + + assert json_response(conn, 200) == %{"is_admin" => true} + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin" + end + + test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do + user = insert(:user, is_admin: true) + + conn = + conn + |> put_req_header("accept", "application/json") + |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin") + + assert json_response(conn, 200) == %{"is_admin" => false} + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} revoked admin role from @#{user.nickname}" + end + + test "/:right DELETE, can remove from a permission group (multiple)", %{ + admin: admin, + conn: conn + } do + user_one = insert(:user, is_admin: true) + user_two = insert(:user, is_admin: true) + + conn = + conn + |> put_req_header("accept", "application/json") + |> delete("/api/pleroma/admin/users/permission_group/admin", %{ + nicknames: [user_one.nickname, user_two.nickname] + }) + + assert json_response(conn, 200) == %{"is_admin" => false} + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{ + user_two.nickname + }" + end + end + + test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset") + + resp = json_response(conn, 200) + + assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"]) + end + + describe "PUT disable_mfa" do + test "returns 200 and disable 2fa", %{conn: conn} do + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true} + } + ) + + response = + conn + |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname}) + |> json_response(200) + + assert response == user.nickname + mfa_settings = refresh_record(user).multi_factor_authentication_settings + + refute mfa_settings.enabled + refute mfa_settings.totp.confirmed + end + + test "returns 404 if user not found", %{conn: conn} do + response = + conn + |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"}) + |> json_response(404) + + assert response == %{"error" => "Not found"} + end + end + + describe "GET /api/pleroma/admin/restart" do + setup do: clear_config(:configurable_from_database, true) + + test "pleroma restarts", %{conn: conn} do + capture_log(fn -> + assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{} + end) =~ "pleroma restarted" + + refute Restarter.Pleroma.need_reboot?() + end + end + + test "need_reboot flag", %{conn: conn} do + assert conn + |> get("/api/pleroma/admin/need_reboot") + |> json_response(200) == %{"need_reboot" => false} + + Restarter.Pleroma.need_reboot() + + assert conn + |> get("/api/pleroma/admin/need_reboot") + |> json_response(200) == %{"need_reboot" => true} + + on_exit(fn -> Restarter.Pleroma.refresh() end) + end + + describe "GET /api/pleroma/admin/users/:nickname/statuses" 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) + + %{user: user} + end + + 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 + 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") + + assert json_response(conn, 200) |> length() == 2 + end + + test "doesn't return private statuses by default", %{conn: conn, user: user} do + {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"}) + + {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) + + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses") + + assert json_response(conn, 200) |> length() == 4 + end + + test "returns private statuses with godmode on", %{conn: conn, user: user} do + {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"}) + + {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) + + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true") + + assert json_response(conn, 200) |> length() == 5 + end + + test "excludes reblogs by default", %{conn: conn, user: user} do + other_user = insert(:user) + {: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 + + conn_res = + get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true") + + assert json_response(conn_res, 200) |> length() == 1 + end + end + + describe "GET /api/pleroma/admin/users/:nickname/chats" do + setup do + user = insert(:user) + recipients = insert_list(3, :user) + + Enum.each(recipients, fn recipient -> + CommonAPI.post_chat_message(user, recipient, "yo") + end) + + %{user: user} + end + + test "renders user's chats", %{conn: conn, user: user} do + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats") + + assert json_response(conn, 200) |> length() == 3 + end + end + + describe "GET /api/pleroma/admin/users/:nickname/chats unauthorized" do + setup do + user = insert(:user) + recipient = insert(:user) + CommonAPI.post_chat_message(user, recipient, "yo") + %{conn: conn} = oauth_access(["read:chats"]) + %{conn: conn, user: user} + end + + test "returns 403", %{conn: conn, user: user} do + conn + |> get("/api/pleroma/admin/users/#{user.nickname}/chats") + |> json_response(403) + end + end + + describe "GET /api/pleroma/admin/users/:nickname/chats unauthenticated" do + setup do + user = insert(:user) + recipient = insert(:user) + CommonAPI.post_chat_message(user, recipient, "yo") + %{conn: build_conn(), user: user} + end + + test "returns 403", %{conn: conn, user: user} do + conn + |> get("/api/pleroma/admin/users/#{user.nickname}/chats") + |> json_response(403) + end + end + + describe "GET /api/pleroma/admin/moderation_log" do + setup do + moderator = insert(:user, is_moderator: true) + + %{moderator: moderator} + end + + test "returns the log", %{conn: conn, admin: admin} do + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_follow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second) + }) + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_unfollow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second) + }) + + conn = get(conn, "/api/pleroma/admin/moderation_log") + + response = json_response(conn, 200) + [first_entry, second_entry] = response["items"] + + assert response["total"] == 2 + assert first_entry["data"]["action"] == "relay_unfollow" + + assert first_entry["message"] == + "@#{admin.nickname} unfollowed relay: https://example.org/relay" + + assert second_entry["data"]["action"] == "relay_follow" + + assert second_entry["message"] == + "@#{admin.nickname} followed relay: https://example.org/relay" + end + + test "returns the log with pagination", %{conn: conn, admin: admin} do + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_follow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second) + }) + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_unfollow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second) + }) + + conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1") + + response1 = json_response(conn1, 200) + [first_entry] = response1["items"] + + assert response1["total"] == 2 + assert response1["items"] |> length() == 1 + assert first_entry["data"]["action"] == "relay_unfollow" + + assert first_entry["message"] == + "@#{admin.nickname} unfollowed relay: https://example.org/relay" + + conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2") + + response2 = json_response(conn2, 200) + [second_entry] = response2["items"] + + assert response2["total"] == 2 + assert response2["items"] |> length() == 1 + assert second_entry["data"]["action"] == "relay_follow" + + assert second_entry["message"] == + "@#{admin.nickname} followed relay: https://example.org/relay" + end + + test "filters log by date", %{conn: conn, admin: admin} do + first_date = "2017-08-15T15:47:06Z" + second_date = "2017-08-20T15:47:06Z" + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_follow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.from_iso8601!(first_date) + }) + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_unfollow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.from_iso8601!(second_date) + }) + + conn1 = + get( + conn, + "/api/pleroma/admin/moderation_log?start_date=#{second_date}" + ) + + response1 = json_response(conn1, 200) + [first_entry] = response1["items"] + + assert response1["total"] == 1 + assert first_entry["data"]["action"] == "relay_unfollow" + + assert first_entry["message"] == + "@#{admin.nickname} unfollowed relay: https://example.org/relay" + end + + test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_follow", + target: "https://example.org/relay" + } + }) + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => moderator.id, + "nickname" => moderator.nickname, + "type" => "user" + }, + action: "relay_unfollow", + target: "https://example.org/relay" + } + }) + + conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}") + + response1 = json_response(conn1, 200) + [first_entry] = response1["items"] + + assert response1["total"] == 1 + assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id + end + + test "returns log filtered by search", %{conn: conn, moderator: moderator} do + ModerationLog.insert_log(%{ + actor: moderator, + action: "relay_follow", + target: "https://example.org/relay" + }) + + ModerationLog.insert_log(%{ + actor: moderator, + action: "relay_unfollow", + target: "https://example.org/relay" + }) + + conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo") + + response1 = json_response(conn1, 200) + [first_entry] = response1["items"] + + assert response1["total"] == 1 + + assert get_in(first_entry, ["data", "message"]) == + "@#{moderator.nickname} unfollowed relay: https://example.org/relay" + end + end + + test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated", + %{conn: conn} do + clear_config(Pleroma.Config.get([:instance, :limit_to_local_content]), :unauthenticated) + user = insert(:user, %{local: false, nickname: "u@peer1.com"}) + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials") + + assert json_response(conn, 200) + end + + describe "GET /users/:nickname/credentials" do + test "gets the user credentials", %{conn: conn} do + user = insert(:user) + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials") + + response = assert json_response(conn, 200) + assert response["email"] == user.email + end + + test "returns 403 if requested by a non-admin" do + user = insert(:user) + + conn = + build_conn() + |> assign(:user, user) + |> get("/api/pleroma/admin/users/#{user.nickname}/credentials") + + assert json_response(conn, :forbidden) + end + end + + describe "PATCH /users/:nickname/credentials" do + setup do + user = insert(:user) + [user: user] + end + + test "changes password and email", %{conn: conn, admin: admin, user: user} do + assert user.password_reset_pending == false + + conn = + patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ + "password" => "new_password", + "email" => "new_email@example.com", + "name" => "new_name" + }) + + assert json_response(conn, 200) == %{"status" => "success"} + + ObanHelpers.perform_all() + + updated_user = User.get_by_id(user.id) + + assert updated_user.email == "new_email@example.com" + assert updated_user.name == "new_name" + assert updated_user.password_hash != user.password_hash + assert updated_user.password_reset_pending == true + + [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort() + + assert ModerationLog.get_log_entry_message(log_entry1) == + "@#{admin.nickname} updated users: @#{user.nickname}" + + assert ModerationLog.get_log_entry_message(log_entry2) == + "@#{admin.nickname} forced password reset for users: @#{user.nickname}" + end + + test "returns 403 if requested by a non-admin", %{user: user} do + conn = + build_conn() + |> assign(:user, user) + |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{ + "password" => "new_password", + "email" => "new_email@example.com", + "name" => "new_name" + }) + + assert json_response(conn, :forbidden) + end + + test "changes actor type from permitted list", %{conn: conn, user: user} do + assert user.actor_type == "Person" + + assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ + "actor_type" => "Service" + }) + |> json_response(200) == %{"status" => "success"} + + updated_user = User.get_by_id(user.id) + + assert updated_user.actor_type == "Service" + + assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ + "actor_type" => "Application" + }) + |> json_response(400) == %{"errors" => %{"actor_type" => "is invalid"}} + end + + test "update non existing user", %{conn: conn} do + assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{ + "password" => "new_password" + }) + |> json_response(404) == %{"error" => "Not found"} + end + end + + describe "PATCH /users/:nickname/force_password_reset" do + test "sets password_reset_pending to true", %{conn: conn} do + user = insert(:user) + assert user.password_reset_pending == false + + conn = + patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]}) + + assert empty_json_response(conn) == "" + + ObanHelpers.perform_all() + + assert User.get_by_id(user.id).password_reset_pending == true + end + end + + describe "instances" do + test "GET /instances/:instance/statuses", %{conn: conn} do + user = insert(:user, local: false, ap_id: "https://archae.me/users/archaeme") + user2 = insert(:user, local: false, ap_id: "https://test.com/users/test") + insert_pair(:note_activity, user: user) + activity = insert(:note_activity, user: user2) + + ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses") + + response = json_response(ret_conn, 200) + + assert length(response) == 2 + + 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) + + 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 + + ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true") + response = json_response(ret_conn, 200) + assert length(response) == 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) + + assert first_user.confirmation_pending == true + assert second_user.confirmation_pending == true + + ret_conn = + patch(conn, "/api/pleroma/admin/users/confirm_email", %{ + nicknames: [ + first_user.nickname, + second_user.nickname + ] + }) + + assert ret_conn.status == 200 + + assert first_user.confirmation_pending == true + assert second_user.confirmation_pending == true + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{ + second_user.nickname + }" + end + end + + 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) + + ret_conn = + patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{ + nicknames: [ + first_user.nickname, + second_user.nickname + ] + }) + + assert ret_conn.status == 200 + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{ + second_user.nickname + }" + + ObanHelpers.perform_all() + + Pleroma.Emails.UserEmail.account_confirmation_email(first_user) + # temporary hackney fix until hackney max_connections bug is fixed + # https://git.pleroma.social/pleroma/pleroma/-/issues/2101 + |> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) + |> assert_email_sent() + end + end + + describe "/api/pleroma/admin/stats" do + test "status visibility count", %{conn: conn} do + admin = insert(:user, is_admin: true) + user = insert(:user) + CommonAPI.post(user, %{visibility: "public", status: "hey"}) + CommonAPI.post(user, %{visibility: "unlisted", status: "hey"}) + CommonAPI.post(user, %{visibility: "unlisted", status: "hey"}) + + response = + conn + |> assign(:user, admin) + |> get("/api/pleroma/admin/stats") + |> json_response(200) + + assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} = + response["status_visibility"] + end + + test "by instance", %{conn: conn} do + admin = insert(:user, is_admin: true) + user1 = insert(:user) + instance2 = "instance2.tld" + user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"}) + + CommonAPI.post(user1, %{visibility: "public", status: "hey"}) + CommonAPI.post(user2, %{visibility: "unlisted", status: "hey"}) + CommonAPI.post(user2, %{visibility: "private", status: "hey"}) + + response = + conn + |> assign(:user, admin) + |> get("/api/pleroma/admin/stats", instance: instance2) + |> json_response(200) + + assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} = + response["status_visibility"] + end + end + + describe "/api/pleroma/backups" do + test "it creates a backup", %{conn: conn} do + admin = %{id: admin_id, nickname: admin_nickname} = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + user = %{id: user_id, nickname: user_nickname} = insert(:user) + + assert "" == + conn + |> assign(:user, admin) + |> assign(:token, token) + |> post("/api/pleroma/admin/backups", %{nickname: user.nickname}) + |> json_response(200) + + assert [backup] = Repo.all(Pleroma.User.Backup) + + ObanHelpers.perform_all() + + email = Pleroma.Emails.UserEmail.backup_is_ready_email(backup, admin.id) + + assert String.contains?(email.html_body, "Admin @#{admin.nickname} requested a full backup") + assert_email_sent(to: {user.name, user.email}, html_body: email.html_body) + + log_message = "@#{admin_nickname} requested account backup for @#{user_nickname}" + + assert [ + %{ + data: %{ + "action" => "create_backup", + "actor" => %{ + "id" => ^admin_id, + "nickname" => ^admin_nickname + }, + "message" => ^log_message, + "subject" => %{ + "id" => ^user_id, + "nickname" => ^user_nickname + } + } + } + ] = Pleroma.ModerationLog |> Repo.all() + end + + test "it doesn't limit admins", %{conn: conn} do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + user = insert(:user) + + assert "" == + conn + |> assign(:user, admin) + |> assign(:token, token) + |> post("/api/pleroma/admin/backups", %{nickname: user.nickname}) + |> json_response(200) + + assert [_backup] = Repo.all(Pleroma.User.Backup) + + assert "" == + conn + |> assign(:user, admin) + |> assign(:token, token) + |> post("/api/pleroma/admin/backups", %{nickname: user.nickname}) + |> json_response(200) + + assert Repo.aggregate(Pleroma.User.Backup, :count) == 2 + end + end +end + +# Needed for testing +defmodule Pleroma.Web.Endpoint.NotReal do +end + +defmodule Pleroma.Captcha.NotReal do +end diff --git a/test/web/admin_api/controllers/chat_controller_test.exs b/test/pleroma/web/admin_api/controllers/chat_controller_test.exs similarity index 99% rename from test/web/admin_api/controllers/chat_controller_test.exs rename to test/pleroma/web/admin_api/controllers/chat_controller_test.exs index bd4c9c9d1..5aefa1e60 100644 --- a/test/web/admin_api/controllers/chat_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/chat_controller_test.exs @@ -9,7 +9,6 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do alias Pleroma.Chat alias Pleroma.Chat.MessageReference - alias Pleroma.Config alias Pleroma.ModerationLog alias Pleroma.Object alias Pleroma.Repo diff --git a/test/web/admin_api/controllers/config_controller_test.exs b/test/pleroma/web/admin_api/controllers/config_controller_test.exs similarity index 100% rename from test/web/admin_api/controllers/config_controller_test.exs rename to test/pleroma/web/admin_api/controllers/config_controller_test.exs diff --git a/test/web/admin_api/controllers/instance_document_controller_test.exs b/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs similarity index 99% rename from test/web/admin_api/controllers/instance_document_controller_test.exs rename to test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs index 5f7b042f6..ce867dd0e 100644 --- a/test/web/admin_api/controllers/instance_document_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs @@ -5,7 +5,6 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do use Pleroma.Web.ConnCase, async: true import Pleroma.Factory - alias Pleroma.Config @dir "test/tmp/instance_static" @default_instance_panel ~s(

Welcome to Pleroma!

) diff --git a/test/web/admin_api/controllers/invite_controller_test.exs b/test/pleroma/web/admin_api/controllers/invite_controller_test.exs similarity index 100% rename from test/web/admin_api/controllers/invite_controller_test.exs rename to test/pleroma/web/admin_api/controllers/invite_controller_test.exs diff --git a/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs b/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs similarity index 100% rename from test/web/admin_api/controllers/media_proxy_cache_controller_test.exs rename to test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs diff --git a/test/web/admin_api/controllers/oauth_app_controller_test.exs b/test/pleroma/web/admin_api/controllers/o_auth_app_controller_test.exs similarity index 99% rename from test/web/admin_api/controllers/oauth_app_controller_test.exs rename to test/pleroma/web/admin_api/controllers/o_auth_app_controller_test.exs index ed7c4172c..f388375d1 100644 --- a/test/web/admin_api/controllers/oauth_app_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/o_auth_app_controller_test.exs @@ -8,7 +8,6 @@ defmodule Pleroma.Web.AdminAPI.OAuthAppControllerTest do import Pleroma.Factory - alias Pleroma.Config alias Pleroma.Web setup do diff --git a/test/web/admin_api/controllers/relay_controller_test.exs b/test/pleroma/web/admin_api/controllers/relay_controller_test.exs similarity index 99% rename from test/web/admin_api/controllers/relay_controller_test.exs rename to test/pleroma/web/admin_api/controllers/relay_controller_test.exs index adadf2b5c..b4c5e7567 100644 --- a/test/web/admin_api/controllers/relay_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/relay_controller_test.exs @@ -7,7 +7,6 @@ defmodule Pleroma.Web.AdminAPI.RelayControllerTest do import Pleroma.Factory - alias Pleroma.Config alias Pleroma.ModerationLog alias Pleroma.Repo alias Pleroma.User diff --git a/test/web/admin_api/controllers/report_controller_test.exs b/test/pleroma/web/admin_api/controllers/report_controller_test.exs similarity index 97% rename from test/web/admin_api/controllers/report_controller_test.exs rename to test/pleroma/web/admin_api/controllers/report_controller_test.exs index 57946e6bb..958e1d3ab 100644 --- a/test/web/admin_api/controllers/report_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/report_controller_test.exs @@ -8,7 +8,6 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do import Pleroma.Factory alias Pleroma.Activity - alias Pleroma.Config alias Pleroma.ModerationLog alias Pleroma.Repo alias Pleroma.ReportNote @@ -38,12 +37,21 @@ test "returns report by its id", %{conn: conn} do status_ids: [activity.id] }) + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{ + content: "this is an admin note" + }) + response = conn |> get("/api/pleroma/admin/reports/#{report_id}") |> json_response_and_validate_schema(:ok) assert response["id"] == report_id + + [notes] = response["notes"] + assert notes["content"] == "this is an admin note" end test "returns 404 when report id is invalid", %{conn: conn} do diff --git a/test/web/admin_api/controllers/status_controller_test.exs b/test/pleroma/web/admin_api/controllers/status_controller_test.exs similarity index 99% rename from test/web/admin_api/controllers/status_controller_test.exs rename to test/pleroma/web/admin_api/controllers/status_controller_test.exs index eff78fb0a..a18ef9e4b 100644 --- a/test/web/admin_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/status_controller_test.exs @@ -8,7 +8,6 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do import Pleroma.Factory alias Pleroma.Activity - alias Pleroma.Config alias Pleroma.ModerationLog alias Pleroma.Repo alias Pleroma.User diff --git a/test/pleroma/web/admin_api/controllers/user_controller_test.exs b/test/pleroma/web/admin_api/controllers/user_controller_test.exs new file mode 100644 index 000000000..5705306c7 --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/user_controller_test.exs @@ -0,0 +1,970 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.UserControllerTest do + use Pleroma.Web.ConnCase + use Oban.Testing, repo: Pleroma.Repo + + import Mock + import Pleroma.Factory + + alias Pleroma.HTML + alias Pleroma.ModerationLog + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web + alias Pleroma.Web.ActivityPub.Relay + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.MediaProxy + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + + :ok + end + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + test "with valid `admin_token` query parameter, skips OAuth scopes check" do + clear_config([:admin_token], "password123") + + user = insert(:user) + + conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123") + + 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}" + + 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 + + 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 + end + end + + describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do + setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) + + 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}" + + 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"]) + + 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 + end + end + + describe "DELETE /api/pleroma/admin/users" do + test "single user", %{admin: admin, conn: conn} do + clear_config([:instance, :federating], true) + + user = + insert(:user, + avatar: %{"url" => [%{"href" => "https://someurl"}]}, + banner: %{"url" => [%{"href" => "https://somebanner"}]}, + bio: "Hello world!", + name: "A guy" + ) + + # Create some activities to check they got deleted later + follower = insert(:user) + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + {:ok, _, _, _} = CommonAPI.follow(user, follower) + {:ok, _, _, _} = CommonAPI.follow(follower, user) + user = Repo.get(User, user.id) + assert user.note_count == 1 + assert user.follower_count == 1 + assert user.following_count == 1 + refute user.deactivated + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end, + perform: fn _, _ -> nil end do + conn = + conn + |> put_req_header("accept", "application/json") + |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}") + + ObanHelpers.perform_all() + + assert User.get_by_nickname(user.nickname).deactivated + + 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] + + user = Repo.get(User, user.id) + assert user.deactivated + + assert user.avatar == %{} + assert user.banner == %{} + assert user.note_count == 0 + assert user.follower_count == 0 + assert user.following_count == 0 + assert user.bio == "" + assert user.name == nil + + assert called(Pleroma.Web.Federator.publish(:_)) + end + end + + test "multiple users", %{admin: admin, conn: conn} do + user_one = insert(:user) + user_two = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> delete("/api/pleroma/admin/users", %{ + nicknames: [user_one.nickname, user_two.nickname] + }) + + 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 = + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users", %{ + "users" => [ + %{ + "nickname" => "lain", + "email" => "lain@example.org", + "password" => "test" + }, + %{ + "nickname" => "lain2", + "email" => "lain2@example.org", + "password" => "test" + } + ] + }) + + response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type")) + assert response == ["success", "success"] + + log_entry = Repo.one(ModerationLog) + + assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == [] + end + + test "Cannot create user with existing email", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users", %{ + "users" => [ + %{ + "nickname" => "lain", + "email" => user.email, + "password" => "test" + } + ] + }) + + assert json_response(conn, 409) == [ + %{ + "code" => 409, + "data" => %{ + "email" => user.email, + "nickname" => "lain" + }, + "error" => "email has already been taken", + "type" => "error" + } + ] + end + + test "Cannot create user with existing nickname", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users", %{ + "users" => [ + %{ + "nickname" => user.nickname, + "email" => "someuser@plerama.social", + "password" => "test" + } + ] + }) + + assert json_response(conn, 409) == [ + %{ + "code" => 409, + "data" => %{ + "email" => "someuser@plerama.social", + "nickname" => user.nickname + }, + "error" => "nickname has already been taken", + "type" => "error" + } + ] + end + + test "Multiple user creation works in transaction", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users", %{ + "users" => [ + %{ + "nickname" => "newuser", + "email" => "newuser@pleroma.social", + "password" => "test" + }, + %{ + "nickname" => "lain", + "email" => user.email, + "password" => "test" + } + ] + }) + + assert json_response(conn, 409) == [ + %{ + "code" => 409, + "data" => %{ + "email" => user.email, + "nickname" => "lain" + }, + "error" => "email has already been taken", + "type" => "error" + }, + %{ + "code" => 409, + "data" => %{ + "email" => "newuser@pleroma.social", + "nickname" => "newuser" + }, + "error" => "", + "type" => "error" + } + ] + + assert User.get_by_nickname("newuser") === nil + end + end + + describe "/api/pleroma/admin/users/:nickname" do + test "Show", %{conn: conn} do + user = insert(:user) + + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") + + assert user_response(user) == json_response(conn, 200) + end + + test "when the user doesn't exist", %{conn: conn} do + user = build(:user) + + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") + + assert %{"error" => "Not found"} == json_response(conn, 404) + end + end + + describe "/api/pleroma/admin/users/follow" do + test "allows to force-follow another user", %{admin: admin, conn: conn} do + user = insert(:user) + follower = insert(:user) + + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users/follow", %{ + "follower" => follower.nickname, + "followed" => user.nickname + }) + + user = User.get_cached_by_id(user.id) + follower = User.get_cached_by_id(follower.id) + + assert User.following?(follower, user) + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}" + end + end + + describe "/api/pleroma/admin/users/unfollow" do + test "allows to force-unfollow another user", %{admin: admin, conn: conn} do + user = insert(:user) + follower = insert(:user) + + User.follow(follower, user) + + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users/unfollow", %{ + "follower" => follower.nickname, + "followed" => user.nickname + }) + + user = User.get_cached_by_id(user.id) + follower = User.get_cached_by_id(follower.id) + + refute User.following?(follower, user) + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}" + end + end + + 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") + + conn = get(conn, "/api/pleroma/admin/users?page=1") + + users = + [ + user_response( + admin, + %{"roles" => %{"admin" => true, "moderator" => false}} + ), + user_response(user, %{"local" => false, "tags" => ["foo", "bar"]}), + user_response( + user2, + %{ + "local" => true, + "approval_pending" => true, + "registration_reason" => "I'm a chill dude", + "actor_type" => "Person" + } + ) + ] + |> Enum.sort_by(& &1["nickname"]) + + assert json_response(conn, 200) == %{ + "count" => 3, + "page_size" => 50, + "users" => users + } + end + + test "pagination works correctly with service users", %{conn: conn} do + service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido") + + insert_list(25, :user) + + assert %{"count" => 26, "page_size" => 10, "users" => users1} = + conn + |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"}) + |> json_response(200) + + assert Enum.count(users1) == 10 + assert service1 not in users1 + + assert %{"count" => 26, "page_size" => 10, "users" => users2} = + conn + |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"}) + |> json_response(200) + + assert Enum.count(users2) == 10 + assert service1 not in users2 + + assert %{"count" => 26, "page_size" => 10, "users" => users3} = + conn + |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"}) + |> json_response(200) + + assert Enum.count(users3) == 6 + assert service1 not in users3 + end + + test "renders empty array for the second page", %{conn: conn} do + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?page=2") + + assert json_response(conn, 200) == %{ + "count" => 2, + "page_size" => 50, + "users" => [] + } + end + + test "regular search", %{conn: conn} do + user = insert(:user, nickname: "bob") + + conn = get(conn, "/api/pleroma/admin/users?query=bo") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [user_response(user, %{"local" => true})] + } + end + + test "search by domain", %{conn: conn} do + user = insert(:user, nickname: "nickname@domain.com") + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?query=domain.com") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [user_response(user)] + } + end + + test "search by full nickname", %{conn: conn} do + user = insert(:user, nickname: "nickname@domain.com") + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [user_response(user)] + } + end + + test "search by display name", %{conn: conn} do + user = insert(:user, name: "Display name") + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?name=display") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [user_response(user)] + } + end + + test "search by email", %{conn: conn} do + user = insert(:user, email: "email@example.com") + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?email=email@example.com") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [user_response(user)] + } + end + + test "regular search with page size", %{conn: conn} do + user = insert(:user, nickname: "aalice") + user2 = insert(:user, nickname: "alice") + + conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1") + + assert json_response(conn1, 200) == %{ + "count" => 2, + "page_size" => 1, + "users" => [user_response(user)] + } + + conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2") + + assert json_response(conn2, 200) == %{ + "count" => 2, + "page_size" => 1, + "users" => [user_response(user2)] + } + end + + test "only local users" do + admin = insert(:user, is_admin: true, nickname: "john") + token = insert(:oauth_admin_token, user: admin) + user = insert(:user, nickname: "bob") + + insert(:user, nickname: "bobb", local: false) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + |> get("/api/pleroma/admin/users?query=bo&filters=local") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [user_response(user)] + } + end + + test "only local users with no query", %{conn: conn, admin: old_admin} do + admin = insert(:user, is_admin: true, nickname: "john") + user = insert(:user, nickname: "bob") + + insert(:user, nickname: "bobb", local: false) + + conn = get(conn, "/api/pleroma/admin/users?filters=local") + + users = + [ + user_response(user), + user_response(admin, %{ + "roles" => %{"admin" => true, "moderator" => false} + }), + user_response(old_admin, %{ + "deactivated" => false, + "roles" => %{"admin" => true, "moderator" => false} + }) + ] + |> Enum.sort_by(& &1["nickname"]) + + assert json_response(conn, 200) == %{ + "count" => 3, + "page_size" => 50, + "users" => users + } + 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) + + insert(:user, nickname: "happyboy", approval_pending: false) + insert(:user, confirmation_pending: false) + + result = + conn + |> get("/api/pleroma/admin/users?filters=unconfirmed") + |> json_response(200) + + users = + Enum.map([old_user, sad_user], fn user -> + user_response(user, %{ + "confirmation_pending" => true, + "approval_pending" => false + }) + end) + |> Enum.sort_by(& &1["nickname"]) + + assert result == %{"count" => 2, "page_size" => 50, "users" => users} + end + + test "only unapproved users", %{conn: conn} do + user = + insert(:user, + nickname: "sadboy", + approval_pending: true, + registration_reason: "Plz let me in!" + ) + + insert(:user, nickname: "happyboy", approval_pending: false) + + conn = get(conn, "/api/pleroma/admin/users?filters=need_approval") + + users = [ + user_response( + user, + %{"approval_pending" => true, "registration_reason" => "Plz let me in!"} + ) + ] + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => users + } + end + + test "load only admins", %{conn: conn, admin: admin} do + second_admin = insert(:user, is_admin: true) + insert(:user) + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?filters=is_admin") + + users = + [ + user_response(admin, %{ + "deactivated" => false, + "roles" => %{"admin" => true, "moderator" => false} + }), + user_response(second_admin, %{ + "deactivated" => false, + "roles" => %{"admin" => true, "moderator" => false} + }) + ] + |> Enum.sort_by(& &1["nickname"]) + + assert json_response(conn, 200) == %{ + "count" => 2, + "page_size" => 50, + "users" => users + } + end + + test "load only moderators", %{conn: conn} do + moderator = insert(:user, is_moderator: true) + insert(:user) + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + user_response(moderator, %{ + "deactivated" => false, + "roles" => %{"admin" => false, "moderator" => true} + }) + ] + } + end + + test "load users with actor_type is Person", %{admin: admin, conn: conn} do + insert(:user, actor_type: "Service") + insert(:user, actor_type: "Application") + + user1 = insert(:user) + user2 = insert(:user) + + response = + conn + |> get(user_path(conn, :list), %{actor_types: ["Person"]}) + |> json_response(200) + + users = + [ + user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}}), + user_response(user1), + user_response(user2) + ] + |> Enum.sort_by(& &1["nickname"]) + + assert response == %{"count" => 3, "page_size" => 50, "users" => users} + end + + test "load users with actor_type is Person and Service", %{admin: admin, conn: conn} do + user_service = insert(:user, actor_type: "Service") + insert(:user, actor_type: "Application") + + user1 = insert(:user) + user2 = insert(:user) + + response = + conn + |> get(user_path(conn, :list), %{actor_types: ["Person", "Service"]}) + |> json_response(200) + + users = + [ + user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}}), + user_response(user1), + user_response(user2), + user_response(user_service, %{"actor_type" => "Service"}) + ] + |> Enum.sort_by(& &1["nickname"]) + + assert response == %{"count" => 4, "page_size" => 50, "users" => users} + end + + test "load users with actor_type is Service", %{conn: conn} do + user_service = insert(:user, actor_type: "Service") + insert(:user, actor_type: "Application") + insert(:user) + insert(:user) + + response = + conn + |> get(user_path(conn, :list), %{actor_types: ["Service"]}) + |> json_response(200) + + users = [user_response(user_service, %{"actor_type" => "Service"})] + + assert response == %{"count" => 1, "page_size" => 50, "users" => users} + end + + test "load users with tags list", %{conn: conn} do + user1 = insert(:user, tags: ["first"]) + user2 = insert(:user, tags: ["second"]) + insert(:user) + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second") + + users = + [ + user_response(user1, %{"tags" => ["first"]}), + user_response(user2, %{"tags" => ["second"]}) + ] + |> Enum.sort_by(& &1["nickname"]) + + assert json_response(conn, 200) == %{ + "count" => 2, + "page_size" => 50, + "users" => users + } + end + + test "`active` filters out users pending approval", %{token: token} do + insert(:user, approval_pending: true) + %{id: user_id} = insert(:user, approval_pending: false) + %{id: admin_id} = token.user + + conn = + build_conn() + |> assign(:user, token.user) + |> assign(:token, token) + |> get("/api/pleroma/admin/users?filters=active") + + assert %{ + "count" => 2, + "page_size" => 50, + "users" => [ + %{"id" => ^admin_id}, + %{"id" => ^user_id} + ] + } = json_response(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) + + insert(:user, nickname: "ken", local: true, deactivated: true) + insert(:user, nickname: "bobb", local: false, deactivated: false) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + |> get("/api/pleroma/admin/users?filters=deactivated,external") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [user_response(user)] + } + end + + test "it omits relay user", %{admin: admin, conn: conn} do + assert %User{} = Relay.get_actor() + + conn = get(conn, "/api/pleroma/admin/users") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}}) + ] + } + end + 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) + + conn = + patch( + conn, + "/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] + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}" + 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) + + conn = + patch( + conn, + "/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] + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}" + 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) + + conn = + patch( + conn, + "/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] + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}" + end + + 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") + + assert json_response(conn, 200) == + user_response( + user, + %{"deactivated" => !user.deactivated} + ) + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} deactivated users: @#{user.nickname}" + end + + defp user_response(user, attrs \\ %{}) do + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "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, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + |> Map.merge(attrs) + end +end diff --git a/test/web/admin_api/search_test.exs b/test/pleroma/web/admin_api/search_test.exs similarity index 83% rename from test/web/admin_api/search_test.exs rename to test/pleroma/web/admin_api/search_test.exs index d88867c52..92a116c65 100644 --- a/test/web/admin_api/search_test.exs +++ b/test/pleroma/web/admin_api/search_test.exs @@ -143,6 +143,20 @@ test "it returns users with tags" do assert user2 in users end + test "it returns users by actor_types" do + user_service = insert(:user, actor_type: "Service") + user_application = insert(:user, actor_type: "Application") + user1 = insert(:user) + user2 = insert(:user) + + {:ok, [^user_service], 1} = Search.user(%{actor_types: ["Service"]}) + {:ok, [^user_application], 1} = Search.user(%{actor_types: ["Application"]}) + {:ok, [^user1, ^user2], 2} = Search.user(%{actor_types: ["Person"]}) + + {:ok, [^user_service, ^user1, ^user2], 3} = + Search.user(%{actor_types: ["Person", "Service"]}) + end + test "it returns user by display name" do user = insert(:user, name: "Display name") insert(:user) @@ -178,9 +192,20 @@ test "it returns unapproved user" do assert count == 1 end + test "it returns unconfirmed user" do + unconfirmed = insert(:user, confirmation_pending: true) + insert(:user) + insert(:user) + + {:ok, _results, total} = Search.user() + {:ok, [^unconfirmed], count} = Search.user(%{unconfirmed: true}) + assert total == 3 + assert count == 1 + end + test "it returns non-discoverable users" do insert(:user) - insert(:user, discoverable: false) + insert(:user, is_discoverable: false) {:ok, _results, total} = Search.user() diff --git a/test/web/admin_api/views/report_view_test.exs b/test/pleroma/web/admin_api/views/report_view_test.exs similarity index 100% rename from test/web/admin_api/views/report_view_test.exs rename to test/pleroma/web/admin_api/views/report_view_test.exs diff --git a/test/web/api_spec/schema_examples_test.exs b/test/pleroma/web/api_spec/schema_examples_test.exs similarity index 100% rename from test/web/api_spec/schema_examples_test.exs rename to test/pleroma/web/api_spec/schema_examples_test.exs diff --git a/test/web/auth/auth_test_controller_test.exs b/test/pleroma/web/auth/auth_controller_test.exs similarity index 99% rename from test/web/auth/auth_test_controller_test.exs rename to test/pleroma/web/auth/auth_controller_test.exs index fed52b7f3..498554060 100644 --- a/test/web/auth/auth_test_controller_test.exs +++ b/test/pleroma/web/auth/auth_controller_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Tests.AuthTestControllerTest do +defmodule Pleroma.Web.Auth.AuthControllerTest do use Pleroma.Web.ConnCase import Pleroma.Factory diff --git a/test/web/auth/authenticator_test.exs b/test/pleroma/web/auth/authenticator_test.exs similarity index 100% rename from test/web/auth/authenticator_test.exs rename to test/pleroma/web/auth/authenticator_test.exs diff --git a/test/web/auth/basic_auth_test.exs b/test/pleroma/web/auth/basic_auth_test.exs similarity index 100% rename from test/web/auth/basic_auth_test.exs rename to test/pleroma/web/auth/basic_auth_test.exs diff --git a/test/web/auth/pleroma_authenticator_test.exs b/test/pleroma/web/auth/pleroma_authenticator_test.exs similarity index 100% rename from test/web/auth/pleroma_authenticator_test.exs rename to test/pleroma/web/auth/pleroma_authenticator_test.exs diff --git a/test/web/auth/totp_authenticator_test.exs b/test/pleroma/web/auth/totp_authenticator_test.exs similarity index 100% rename from test/web/auth/totp_authenticator_test.exs rename to test/pleroma/web/auth/totp_authenticator_test.exs diff --git a/test/web/chat_channel_test.exs b/test/pleroma/web/chat_channel_test.exs similarity index 87% rename from test/web/chat_channel_test.exs rename to test/pleroma/web/chat_channel_test.exs index f18f3a212..32170873d 100644 --- a/test/web/chat_channel_test.exs +++ b/test/pleroma/web/chat_channel_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ChatChannelTest do use Pleroma.Web.ChannelCase alias Pleroma.Web.ChatChannel diff --git a/test/web/common_api/common_api_utils_test.exs b/test/pleroma/web/common_api/utils_test.exs similarity index 100% rename from test/web/common_api/common_api_utils_test.exs rename to test/pleroma/web/common_api/utils_test.exs diff --git a/test/web/common_api/common_api_test.exs b/test/pleroma/web/common_api_test.exs similarity index 97% rename from test/web/common_api/common_api_test.exs rename to test/pleroma/web/common_api_test.exs index 45ab1e0bd..8e87e69fe 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/pleroma/web/common_api_test.exs @@ -29,6 +29,23 @@ defmodule Pleroma.Web.CommonAPITest do setup do: clear_config([:instance, :limit]) setup do: clear_config([:instance, :max_pinned_statuses]) + describe "posting polls" do + test "it posts a poll" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "who is the best", + poll: %{expires_in: 600, options: ["reimu", "marisa"]} + }) + + object = Object.normalize(activity) + + assert object.data["type"] == "Question" + assert object.data["oneOf"] |> length() == 2 + end + end + describe "blocking" do setup do blocker = insert(:user) @@ -78,12 +95,26 @@ test "it blocks and does not federate if outgoing blocks are disabled", %{ describe "posting chat messages" do setup do: clear_config([:instance, :chat_limit]) + test "it posts a self-chat" do + author = insert(:user) + recipient = author + + {:ok, activity} = + CommonAPI.post_chat_message( + author, + recipient, + "remember to buy milk when milk truk arive" + ) + + assert activity.data["type"] == "Create" + end + test "it posts a chat message without content but with an attachment" do author = insert(:user) recipient = insert(:user) file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -605,7 +636,7 @@ test "it validates character limits are correctly enforced" do assert {:error, "The status is over the character limit"} = CommonAPI.post(user, %{status: "foobar"}) - assert {:ok, activity} = CommonAPI.post(user, %{status: "12345"}) + assert {:ok, _activity} = CommonAPI.post(user, %{status: "12345"}) end test "it can handle activities that expire" do @@ -1076,7 +1107,7 @@ test "also unsubscribes a user" do test "cancels a pending follow for a local user" do follower = insert(:user) - followed = insert(:user, locked: true) + followed = insert(:user, is_locked: true) assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} = CommonAPI.follow(follower, followed) @@ -1098,7 +1129,7 @@ test "cancels a pending follow for a local user" do test "cancels a pending follow for a remote user" do follower = insert(:user) - followed = insert(:user, locked: true, local: false, ap_enabled: true) + followed = insert(:user, is_locked: true, local: false, ap_enabled: true) assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} = CommonAPI.follow(follower, followed) @@ -1121,7 +1152,7 @@ test "cancels a pending follow for a remote user" do describe "accept_follow_request/2" do test "after acceptance, it sets all existing pending follow request states to 'accept'" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) follower = insert(:user) follower_two = insert(:user) @@ -1141,7 +1172,7 @@ test "after acceptance, it sets all existing pending follow request states to 'a end test "after rejection, it sets all existing pending follow request states to 'reject'" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) follower = insert(:user) follower_two = insert(:user) @@ -1161,7 +1192,7 @@ test "after rejection, it sets all existing pending follow request states to 're end test "doesn't create a following relationship if the corresponding follow request doesn't exist" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) not_follower = insert(:user) CommonAPI.accept_follow_request(not_follower, user) diff --git a/test/pleroma/web/endpoint/metrics_exporter_test.exs b/test/pleroma/web/endpoint/metrics_exporter_test.exs new file mode 100644 index 000000000..875addc96 --- /dev/null +++ b/test/pleroma/web/endpoint/metrics_exporter_test.exs @@ -0,0 +1,68 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Endpoint.MetricsExporterTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.Endpoint.MetricsExporter + + defp config do + Application.get_env(:prometheus, MetricsExporter) + end + + describe "with default config" do + test "does NOT expose app metrics", %{conn: conn} do + conn + |> get(config()[:path]) + |> json_response(404) + end + end + + describe "when enabled" do + setup do + initial_config = config() + on_exit(fn -> Application.put_env(:prometheus, MetricsExporter, initial_config) end) + + Application.put_env( + :prometheus, + MetricsExporter, + Keyword.put(initial_config, :enabled, true) + ) + end + + test "serves app metrics", %{conn: conn} do + conn = get(conn, config()[:path]) + assert response = response(conn, 200) + + for metric <- [ + "http_requests_total", + "http_request_duration_microseconds", + "phoenix_controller_call_duration", + "telemetry_scrape_duration", + "erlang_vm_memory_atom_bytes_total" + ] do + assert response =~ ~r/#{metric}/ + end + end + + test "when IP whitelist configured, " <> + "serves app metrics only if client IP is whitelisted", + %{conn: conn} do + Application.put_env( + :prometheus, + MetricsExporter, + Keyword.put(config(), :ip_whitelist, ["127.127.127.127", {1, 1, 1, 1}, '255.255.255.255']) + ) + + conn + |> get(config()[:path]) + |> json_response(404) + + conn + |> Map.put(:remote_ip, {127, 127, 127, 127}) + |> get(config()[:path]) + |> response(200) + end + end +end diff --git a/test/web/fallback_test.exs b/test/pleroma/web/fallback_test.exs similarity index 100% rename from test/web/fallback_test.exs rename to test/pleroma/web/fallback_test.exs diff --git a/test/web/fed_sockets/fed_registry_test.exs b/test/pleroma/web/fed_sockets/fed_registry_test.exs similarity index 96% rename from test/web/fed_sockets/fed_registry_test.exs rename to test/pleroma/web/fed_sockets/fed_registry_test.exs index 19ac874d6..73aaced46 100644 --- a/test/web/fed_sockets/fed_registry_test.exs +++ b/test/pleroma/web/fed_sockets/fed_registry_test.exs @@ -52,7 +52,7 @@ test "multiple origins can be added" do end test "will be ignored" do - assert {:ok, %SocketInfo{origin: origin, pid: pid_one}} = + assert {:ok, %SocketInfo{origin: origin, pid: _pid_one}} = FedRegistry.get_fed_socket(@good_domain_origin) assert origin == "good.domain:80" @@ -63,7 +63,7 @@ test "will be ignored" do test "the newer process will be closed" do pid_two = build_test_socket(@good_domain) - assert {:ok, %SocketInfo{origin: origin, pid: pid_one}} = + assert {:ok, %SocketInfo{origin: origin, pid: _pid_one}} = FedRegistry.get_fed_socket(@good_domain_origin) assert origin == "good.domain:80" diff --git a/test/web/fed_sockets/fetch_registry_test.exs b/test/pleroma/web/fed_sockets/fetch_registry_test.exs similarity index 100% rename from test/web/fed_sockets/fetch_registry_test.exs rename to test/pleroma/web/fed_sockets/fetch_registry_test.exs diff --git a/test/web/fed_sockets/socket_info_test.exs b/test/pleroma/web/fed_sockets/socket_info_test.exs similarity index 100% rename from test/web/fed_sockets/socket_info_test.exs rename to test/pleroma/web/fed_sockets/socket_info_test.exs diff --git a/test/web/federator_test.exs b/test/pleroma/web/federator_test.exs similarity index 100% rename from test/web/federator_test.exs rename to test/pleroma/web/federator_test.exs diff --git a/test/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs similarity index 95% rename from test/web/feed/tag_controller_test.exs rename to test/pleroma/web/feed/tag_controller_test.exs index 868e40965..e4084b0e5 100644 --- a/test/web/feed/tag_controller_test.exs +++ b/test/pleroma/web/feed/tag_controller_test.exs @@ -8,6 +8,7 @@ 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 @@ -15,7 +16,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do setup do: clear_config([:feed]) test "gets a feed (ATOM)", %{conn: conn} do - Pleroma.Config.put( + Config.put( [:feed, :post_title], %{max_length: 25, omission: "..."} ) @@ -82,7 +83,7 @@ test "gets a feed (ATOM)", %{conn: conn} do end test "gets a feed (RSS)", %{conn: conn} do - Pleroma.Config.put( + Config.put( [:feed, :post_title], %{max_length: 25, omission: "..."} ) @@ -157,7 +158,7 @@ test "gets a feed (RSS)", %{conn: conn} do response = conn |> put_req_header("accept", "application/rss+xml") - |> get(tag_feed_path(conn, :feed, "pleromaart")) + |> get(tag_feed_path(conn, :feed, "pleromaart.rss")) |> response(200) xml = parse(response) @@ -183,14 +184,12 @@ test "gets a feed (RSS)", %{conn: conn} do end describe "private instance" do - setup do: clear_config([:instance, :public]) + setup do: clear_config([:instance, :public], false) test "returns 404 for tags feed", %{conn: conn} do - Config.put([:instance, :public], false) - conn |> put_req_header("accept", "application/rss+xml") - |> get(tag_feed_path(conn, :feed, "pleromaart")) + |> get(tag_feed_path(conn, :feed, "pleromaart.rss")) |> response(404) end end diff --git a/test/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs similarity index 94% rename from test/web/feed/user_controller_test.exs rename to test/pleroma/web/feed/user_controller_test.exs index 9a5610baa..eabfe3a63 100644 --- a/test/web/feed/user_controller_test.exs +++ b/test/pleroma/web/feed/user_controller_test.exs @@ -13,7 +13,7 @@ defmodule Pleroma.Web.Feed.UserControllerTest do alias Pleroma.User alias Pleroma.Web.CommonAPI - setup do: clear_config([:instance, :federating], true) + setup do: clear_config([:static_fe, :enabled], false) describe "feed" do setup do: clear_config([:feed]) @@ -192,6 +192,16 @@ test "returns 404 when the user is remote", %{conn: conn} do |> get(user_feed_path(conn, :feed, user.nickname)) |> response(404) end + + test "does not require authentication on non-federating instances", %{conn: conn} do + clear_config([:instance, :federating], false) + user = insert(:user) + + conn + |> put_req_header("accept", "application/rss+xml") + |> get("/users/#{user.nickname}/feed.rss") + |> response(200) + end end # Note: see ActivityPubControllerTest for JSON format tests @@ -206,7 +216,7 @@ test "with html format, it redirects to user feed", %{conn: conn} do |> response(200) assert response == - Fallback.RedirectController.redirector_with_meta( + Pleroma.Web.Fallback.RedirectController.redirector_with_meta( conn, %{user: user} ).resp_body diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs similarity index 94% rename from test/web/mastodon_api/controllers/account_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index f7f1369e4..58ce76ab8 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -32,7 +32,7 @@ test "works by id" do test "works by nickname" do user = insert(:user) - assert %{"id" => user_id} = + assert %{"id" => _user_id} = build_conn() |> get("/api/v1/accounts/#{user.nickname}") |> json_response_and_validate_schema(200) @@ -43,7 +43,7 @@ test "works by nickname for remote users" do user = insert(:user, nickname: "user@example.com", local: false) - assert %{"id" => user_id} = + assert %{"id" => _user_id} = build_conn() |> get("/api/v1/accounts/#{user.nickname}") |> json_response_and_validate_schema(200) @@ -380,7 +380,7 @@ test "gets an users media, excludes reblogs", %{conn: conn} do other_user = insert(:user) file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -706,7 +706,7 @@ test "following / unfollowing a user", %{conn: conn} do end test "cancelling follow request", %{conn: conn} do - %{id: other_user_id} = insert(:user, %{locked: true}) + %{id: other_user_id} = insert(:user, %{is_locked: true}) assert %{"id" => ^other_user_id, "following" => false, "requested" => true} = conn @@ -1429,10 +1429,10 @@ test "returns an error if captcha is invalid", %{conn: conn} do test "returns lists to which the account belongs" do %{user: user, conn: conn} = oauth_access(["read:lists"]) other_user = insert(:user) - assert {:ok, %Pleroma.List{id: list_id} = list} = Pleroma.List.create("Test List", user) + assert {:ok, %Pleroma.List{id: _list_id} = list} = Pleroma.List.create("Test List", user) {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user) - assert [%{"id" => list_id, "title" => "Test List"}] = + assert [%{"id" => _list_id, "title" => "Test List"}] = conn |> get("/api/v1/accounts/#{other_user.id}/lists") |> json_response_and_validate_schema(200) @@ -1509,28 +1509,103 @@ test "returns an empty list on a bad request", %{conn: conn} do test "getting a list of mutes" do %{user: user, conn: conn} = oauth_access(["read:mutes"]) - other_user = insert(:user) + %{id: id1} = other_user1 = insert(:user) + %{id: id2} = other_user2 = insert(:user) + %{id: id3} = other_user3 = insert(:user) - {:ok, _user_relationships} = User.mute(user, other_user) + {:ok, _user_relationships} = User.mute(user, other_user1) + {:ok, _user_relationships} = User.mute(user, other_user2) + {:ok, _user_relationships} = User.mute(user, other_user3) - conn = get(conn, "/api/v1/mutes") + result = + conn + |> assign(:user, user) + |> get("/api/v1/mutes") + |> json_response_and_validate_schema(200) - other_user_id = to_string(other_user.id) - assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200) + assert [id1, id2, id3] == Enum.map(result, & &1["id"]) + + result = + conn + |> assign(:user, user) + |> get("/api/v1/mutes?limit=1") + |> json_response_and_validate_schema(200) + + assert [%{"id" => ^id1}] = result + + result = + conn + |> assign(:user, user) + |> get("/api/v1/mutes?since_id=#{id1}") + |> json_response_and_validate_schema(200) + + assert [%{"id" => ^id2}, %{"id" => ^id3}] = result + + result = + conn + |> assign(:user, user) + |> get("/api/v1/mutes?since_id=#{id1}&max_id=#{id3}") + |> json_response_and_validate_schema(200) + + assert [%{"id" => ^id2}] = result + + 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 "getting a list of blocks" do %{user: user, conn: conn} = oauth_access(["read:blocks"]) - other_user = insert(:user) + %{id: id1} = other_user1 = insert(:user) + %{id: id2} = other_user2 = insert(:user) + %{id: id3} = other_user3 = insert(:user) - {:ok, _user_relationship} = User.block(user, other_user) + {:ok, _user_relationship} = User.block(user, other_user1) + {:ok, _user_relationship} = User.block(user, other_user3) + {:ok, _user_relationship} = User.block(user, other_user2) - conn = + result = conn |> assign(:user, user) |> get("/api/v1/blocks") + |> json_response_and_validate_schema(200) - other_user_id = to_string(other_user.id) - assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200) + assert [id1, id2, id3] == Enum.map(result, & &1["id"]) + + result = + conn + |> assign(:user, user) + |> get("/api/v1/blocks?limit=1") + |> json_response_and_validate_schema(200) + + assert [%{"id" => ^id1}] = result + + result = + conn + |> assign(:user, user) + |> get("/api/v1/blocks?since_id=#{id1}") + |> json_response_and_validate_schema(200) + + assert [%{"id" => ^id2}, %{"id" => ^id3}] = result + + result = + conn + |> assign(:user, user) + |> get("/api/v1/blocks?since_id=#{id1}&max_id=#{id3}") + |> json_response_and_validate_schema(200) + + assert [%{"id" => ^id2}] = result + + result = + conn + |> assign(:user, user) + |> get("/api/v1/blocks?since_id=#{id1}&limit=1") + |> json_response_and_validate_schema(200) + + assert [%{"id" => ^id2}] = result end end diff --git a/test/web/mastodon_api/controllers/app_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/app_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/app_controller_test.exs diff --git a/test/web/mastodon_api/controllers/auth_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/auth_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/auth_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/auth_controller_test.exs diff --git a/test/web/mastodon_api/controllers/conversation_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs similarity index 83% rename from test/web/mastodon_api/controllers/conversation_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs index 3e21e6bf1..c67e584dd 100644 --- a/test/web/mastodon_api/controllers/conversation_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs @@ -5,6 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do use Pleroma.Web.ConnCase + alias Pleroma.Conversation.Participation alias Pleroma.User alias Pleroma.Web.CommonAPI @@ -28,10 +29,10 @@ test "returns correct conversations", %{ user_three: user_three, conn: conn } do - assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 + assert Participation.unread_count(user_two) == 0 {:ok, direct} = create_direct_message(user_one, [user_two, user_three]) - assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 + assert Participation.unread_count(user_two) == 1 {:ok, _follower_only} = CommonAPI.post(user_one, %{ @@ -54,12 +55,33 @@ test "returns correct conversations", %{ account_ids = Enum.map(res_accounts, & &1["id"]) assert length(res_accounts) == 2 + assert user_one.id not in account_ids assert user_two.id in account_ids assert user_three.id in account_ids assert is_binary(res_id) assert unread == false assert res_last_status["id"] == direct.id - assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 + assert res_last_status["account"]["id"] == user_one.id + assert Participation.unread_count(user_one) == 0 + end + + test "includes the user if the user is the only participant", %{ + user: user_one, + conn: conn + } do + {:ok, _direct} = create_direct_message(user_one, []) + + res_conn = get(conn, "/api/v1/conversations") + + assert response = json_response_and_validate_schema(res_conn, 200) + + assert [ + %{ + "accounts" => [account] + } + ] = response + + assert user_one.id == account["id"] end test "observes limit params", %{ @@ -134,8 +156,8 @@ test "the user marks a conversation as read", %{user: user_one, conn: conn} do user_two = insert(:user) {:ok, direct} = create_direct_message(user_one, [user_two]) - assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 - assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 + assert Participation.unread_count(user_one) == 0 + assert Participation.unread_count(user_two) == 1 user_two_conn = build_conn() @@ -155,8 +177,8 @@ test "the user marks a conversation as read", %{user: user_one, conn: conn} do |> post("/api/v1/conversations/#{direct_conversation_id}/read") |> json_response_and_validate_schema(200) - assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 - assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 + assert Participation.unread_count(user_one) == 0 + assert Participation.unread_count(user_two) == 0 # The conversation is marked as unread on reply {:ok, _} = @@ -171,8 +193,8 @@ test "the user marks a conversation as read", %{user: user_one, conn: conn} do |> get("/api/v1/conversations") |> json_response_and_validate_schema(200) - assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1 - assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 + assert Participation.unread_count(user_one) == 1 + assert Participation.unread_count(user_two) == 0 # A reply doesn't increment the user's unread_conversation_count if the conversation is unread {:ok, _} = @@ -182,8 +204,8 @@ test "the user marks a conversation as read", %{user: user_one, conn: conn} do in_reply_to_status_id: direct.id }) - assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1 - assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 + assert Participation.unread_count(user_one) == 1 + assert Participation.unread_count(user_two) == 0 end test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do diff --git a/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/custom_emoji_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/custom_emoji_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/custom_emoji_controller_test.exs diff --git a/test/web/mastodon_api/controllers/domain_block_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/domain_block_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/domain_block_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/domain_block_controller_test.exs diff --git a/test/web/mastodon_api/controllers/filter_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/filter_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/filter_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/filter_controller_test.exs diff --git a/test/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs similarity index 98% rename from test/web/mastodon_api/controllers/follow_request_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs index 6749e0e83..a9dd7cd30 100644 --- a/test/web/mastodon_api/controllers/follow_request_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -12,7 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do describe "locked accounts" do setup do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) %{conn: conn} = oauth_access(["follow"], user: user) %{user: user, conn: conn} end diff --git a/test/web/mastodon_api/controllers/instance_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/instance_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs diff --git a/test/web/mastodon_api/controllers/list_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/list_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/list_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/list_controller_test.exs diff --git a/test/web/mastodon_api/controllers/marker_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/marker_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/marker_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/marker_controller_test.exs diff --git a/test/web/mastodon_api/controllers/media_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs similarity index 97% rename from test/web/mastodon_api/controllers/media_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/media_controller_test.exs index 906fd940f..d2bd57515 100644 --- a/test/web/mastodon_api/controllers/media_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs @@ -14,7 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do setup do image = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -74,7 +74,7 @@ test "/api/v2/media", %{conn: conn, user: user, image: image} do setup %{user: actor} do file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -106,7 +106,7 @@ test "/api/v1/media/:id good request", %{conn: conn, object: object} do setup %{user: actor} do file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/notification_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs diff --git a/test/web/mastodon_api/controllers/poll_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/poll_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs diff --git a/test/web/mastodon_api/controllers/report_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/report_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/report_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/report_controller_test.exs diff --git a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs diff --git a/test/web/mastodon_api/controllers/search_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/search_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/search_controller_test.exs diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs similarity index 99% rename from test/web/mastodon_api/controllers/status_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/status_controller_test.exs index 633a25e50..436608e51 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -167,7 +167,7 @@ test "Get MRF reason when posting a status is rejected by one", %{conn: conn} do test "posting an undefined status with an attachment", %{user: user, conn: conn} do file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -408,7 +408,7 @@ test "creates a scheduled activity with a media attachment", %{user: user, conn: |> Kernel.<>("Z") file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -937,7 +937,7 @@ test "reblogged status for another user" do |> get("/api/v1/statuses/#{reblog_activity1.id}") assert %{ - "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2}, + "reblog" => %{"id" => _id, "reblogged" => false, "reblogs_count" => 2}, "reblogged" => false, "favourited" => false, "bookmarked" => false diff --git a/test/web/mastodon_api/controllers/subscription_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/subscription_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs diff --git a/test/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs similarity index 100% rename from test/web/mastodon_api/controllers/suggestion_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs similarity index 97% rename from test/web/mastodon_api/controllers/timeline_controller_test.exs rename to test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs index c6e0268fd..4c08ad60a 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs @@ -8,7 +8,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do import Pleroma.Factory import Tesla.Mock - alias Pleroma.Config alias Pleroma.User alias Pleroma.Web.CommonAPI @@ -148,6 +147,18 @@ test "doesn't return replies if follow is posting with users from blocked domain activities = json_response_and_validate_schema(res_conn, 200) [%{"id" => ^activity_id}] = activities end + + test "can be filtered by instance", %{conn: conn} do + user = insert(:user, ap_id: "https://lain.com/users/lain") + insert(:note_activity, local: false) + insert(:note_activity, local: false) + + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + + conn = get(conn, "/api/v1/timelines/public?instance=lain.com") + + assert length(json_response_and_validate_schema(conn, :ok)) == 1 + end end defp local_and_remote_activities do diff --git a/test/web/masto_fe_controller_test.exs b/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs similarity index 97% rename from test/web/masto_fe_controller_test.exs rename to test/pleroma/web/mastodon_api/masto_fe_controller_test.exs index f3b54b5f2..ed8add8d2 100644 --- a/test/web/masto_fe_controller_test.exs +++ b/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.MastodonAPI.MastoFEController do +defmodule Pleroma.Web.MastodonAPI.MastoFEControllerTest do use Pleroma.Web.ConnCase alias Pleroma.Config diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/pleroma/web/mastodon_api/mastodon_api_controller_test.exs similarity index 100% rename from test/web/mastodon_api/mastodon_api_controller_test.exs rename to test/pleroma/web/mastodon_api/mastodon_api_controller_test.exs diff --git a/test/web/mastodon_api/mastodon_api_test.exs b/test/pleroma/web/mastodon_api/mastodon_api_test.exs similarity index 100% rename from test/web/mastodon_api/mastodon_api_test.exs rename to test/pleroma/web/mastodon_api/mastodon_api_test.exs diff --git a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs b/test/pleroma/web/mastodon_api/update_credentials_test.exs similarity index 98% rename from test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs rename to test/pleroma/web/mastodon_api/update_credentials_test.exs index 2e6704726..ed1921c91 100644 --- a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs +++ b/test/pleroma/web/mastodon_api/update_credentials_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do +defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do alias Pleroma.Repo alias Pleroma.User @@ -222,7 +222,7 @@ test "updates the user's name", %{conn: conn} do test "updates the user's avatar", %{user: user, conn: conn} do new_avatar = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -246,7 +246,7 @@ test "updates the user's avatar", %{user: user, conn: conn} do test "updates the user's banner", %{user: user, conn: conn} do new_header = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -265,7 +265,7 @@ test "updates the user's banner", %{user: user, conn: conn} do test "updates the user's background", %{conn: conn, user: user} do new_header = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs similarity index 98% rename from test/web/mastodon_api/views/account_view_test.exs rename to test/pleroma/web/mastodon_api/views/account_view_test.exs index b5ef1a49f..139e32362 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -332,7 +332,7 @@ test "represent a relationship for the user blocking a domain" do test "represent a relationship for the user with a pending follow request" do user = insert(:user) - other_user = insert(:user, locked: true) + other_user = insert(:user, is_locked: true) {:ok, user, other_user, _} = CommonAPI.follow(user, other_user) user = User.get_cached_by_id(user.id) @@ -481,7 +481,7 @@ test "shows zero when no follow requests are pending" do end test "shows non-zero when follow requests are pending" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) @@ -493,7 +493,7 @@ test "shows non-zero when follow requests are pending" do end test "decreases when accepting a follow request" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) @@ -510,7 +510,7 @@ test "decreases when accepting a follow request" do end test "decreases when rejecting a follow request" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) @@ -527,14 +527,14 @@ test "decreases when rejecting a follow request" do end test "shows non-zero when historical unapproved requests are present" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) other_user = insert(:user) {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - {:ok, user} = User.update_and_set_cache(user, %{locked: false}) + {:ok, user} = User.update_and_set_cache(user, %{is_locked: false}) assert %{locked: false, follow_requests_count: 1} = AccountView.render("show.json", %{user: user, for: user}) diff --git a/test/web/mastodon_api/views/conversation_view_test.exs b/test/pleroma/web/mastodon_api/views/conversation_view_test.exs similarity index 95% rename from test/web/mastodon_api/views/conversation_view_test.exs rename to test/pleroma/web/mastodon_api/views/conversation_view_test.exs index 2e8203c9b..20c10ba3d 100644 --- a/test/web/mastodon_api/views/conversation_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/conversation_view_test.exs @@ -36,9 +36,11 @@ test "represents a Mastodon Conversation entity" do assert conversation.id == participation.id |> to_string() assert conversation.last_status.id == activity.id + assert conversation.last_status.account.id == user.id assert [account] = conversation.accounts assert account.id == other_user.id + assert conversation.last_status.pleroma.direct_conversation_id == participation.id end end diff --git a/test/web/mastodon_api/views/list_view_test.exs b/test/pleroma/web/mastodon_api/views/list_view_test.exs similarity index 100% rename from test/web/mastodon_api/views/list_view_test.exs rename to test/pleroma/web/mastodon_api/views/list_view_test.exs diff --git a/test/web/mastodon_api/views/marker_view_test.exs b/test/pleroma/web/mastodon_api/views/marker_view_test.exs similarity index 100% rename from test/web/mastodon_api/views/marker_view_test.exs rename to test/pleroma/web/mastodon_api/views/marker_view_test.exs diff --git a/test/web/mastodon_api/views/notification_view_test.exs b/test/pleroma/web/mastodon_api/views/notification_view_test.exs similarity index 100% rename from test/web/mastodon_api/views/notification_view_test.exs rename to test/pleroma/web/mastodon_api/views/notification_view_test.exs diff --git a/test/web/mastodon_api/views/poll_view_test.exs b/test/pleroma/web/mastodon_api/views/poll_view_test.exs similarity index 99% rename from test/web/mastodon_api/views/poll_view_test.exs rename to test/pleroma/web/mastodon_api/views/poll_view_test.exs index b7e2f17ef..c655ca438 100644 --- a/test/web/mastodon_api/views/poll_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/poll_view_test.exs @@ -44,7 +44,7 @@ test "renders a poll" do ], voted: false, votes_count: 0, - voters_count: nil + voters_count: 0 } result = PollView.render("show.json", %{object: object}) diff --git a/test/web/mastodon_api/views/scheduled_activity_view_test.exs b/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs similarity index 98% rename from test/web/mastodon_api/views/scheduled_activity_view_test.exs rename to test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs index fbfd873ef..04f73f5a0 100644 --- a/test/web/mastodon_api/views/scheduled_activity_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs @@ -22,7 +22,7 @@ test "A scheduled activity with a media attachment" do |> NaiveDateTime.to_iso8601() file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs similarity index 100% rename from test/web/mastodon_api/views/status_view_test.exs rename to test/pleroma/web/mastodon_api/views/status_view_test.exs diff --git a/test/web/mastodon_api/views/subscription_view_test.exs b/test/pleroma/web/mastodon_api/views/subscription_view_test.exs similarity index 100% rename from test/web/mastodon_api/views/subscription_view_test.exs rename to test/pleroma/web/mastodon_api/views/subscription_view_test.exs diff --git a/test/web/media_proxy/invalidations/http_test.exs b/test/pleroma/web/media_proxy/invalidation/http_test.exs similarity index 88% rename from test/web/media_proxy/invalidations/http_test.exs rename to test/pleroma/web/media_proxy/invalidation/http_test.exs index a1bef5237..13d081325 100644 --- a/test/web/media_proxy/invalidations/http_test.exs +++ b/test/pleroma/web/media_proxy/invalidation/http_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do use ExUnit.Case alias Pleroma.Web.MediaProxy.Invalidation diff --git a/test/web/media_proxy/invalidations/script_test.exs b/test/pleroma/web/media_proxy/invalidation/script_test.exs similarity index 83% rename from test/web/media_proxy/invalidations/script_test.exs rename to test/pleroma/web/media_proxy/invalidation/script_test.exs index 51833ab18..692cbb2df 100644 --- a/test/web/media_proxy/invalidations/script_test.exs +++ b/test/pleroma/web/media_proxy/invalidation/script_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MediaProxy.Invalidation.ScriptTest do use ExUnit.Case alias Pleroma.Web.MediaProxy.Invalidation diff --git a/test/web/media_proxy/invalidation_test.exs b/test/pleroma/web/media_proxy/invalidation_test.exs similarity index 93% rename from test/web/media_proxy/invalidation_test.exs rename to test/pleroma/web/media_proxy/invalidation_test.exs index 926ae74ca..aa1435ac0 100644 --- a/test/web/media_proxy/invalidation_test.exs +++ b/test/pleroma/web/media_proxy/invalidation_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MediaProxy.InvalidationTest do use ExUnit.Case use Pleroma.Tests.Helpers diff --git a/test/web/media_proxy/media_proxy_controller_test.exs b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs similarity index 95% rename from test/web/media_proxy/media_proxy_controller_test.exs rename to test/pleroma/web/media_proxy/media_proxy_controller_test.exs index 33e6873f7..e9b584822 100644 --- a/test/web/media_proxy/media_proxy_controller_test.exs +++ b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs @@ -81,6 +81,15 @@ test "it returns 404 when url is in banned_urls cache", %{conn: conn, url: url} end describe "Media Preview Proxy" do + def assert_dependencies_installed do + missing_dependencies = Pleroma.Helpers.MediaHelper.missing_dependencies() + + assert missing_dependencies == [], + "Error: missing dependencies (please refer to `docs/installation`): #{ + inspect(missing_dependencies) + }" + end + setup do clear_config([:media_proxy, :enabled], true) clear_config([:media_preview_proxy, :enabled], true) @@ -184,6 +193,8 @@ test "with `static=true` and GIF image preview requested, responds with JPEG ima url: url, media_proxy_url: media_proxy_url } do + assert_dependencies_installed() + # Setting a high :min_content_length to ensure this scenario is not affected by its logic clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000) @@ -270,6 +281,8 @@ test "thumbnails PNG images into PNG", %{ url: url, media_proxy_url: media_proxy_url } do + assert_dependencies_installed() + Tesla.Mock.mock(fn %{method: "head", url: ^media_proxy_url} -> %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]} @@ -290,6 +303,8 @@ test "thumbnails JPEG images into JPEG", %{ url: url, media_proxy_url: media_proxy_url } do + assert_dependencies_installed() + Tesla.Mock.mock(fn %{method: "head", url: ^media_proxy_url} -> %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]} diff --git a/test/web/media_proxy/media_proxy_test.exs b/test/pleroma/web/media_proxy_test.exs similarity index 100% rename from test/web/media_proxy/media_proxy_test.exs rename to test/pleroma/web/media_proxy_test.exs diff --git a/test/web/metadata/player_view_test.exs b/test/pleroma/web/metadata/player_view_test.exs similarity index 100% rename from test/web/metadata/player_view_test.exs rename to test/pleroma/web/metadata/player_view_test.exs diff --git a/test/web/metadata/feed_test.exs b/test/pleroma/web/metadata/providers/feed_test.exs similarity index 100% rename from test/web/metadata/feed_test.exs rename to test/pleroma/web/metadata/providers/feed_test.exs diff --git a/test/web/metadata/opengraph_test.exs b/test/pleroma/web/metadata/providers/open_graph_test.exs similarity index 100% rename from test/web/metadata/opengraph_test.exs rename to test/pleroma/web/metadata/providers/open_graph_test.exs diff --git a/test/web/metadata/rel_me_test.exs b/test/pleroma/web/metadata/providers/rel_me_test.exs similarity index 100% rename from test/web/metadata/rel_me_test.exs rename to test/pleroma/web/metadata/providers/rel_me_test.exs diff --git a/test/web/metadata/restrict_indexing_test.exs b/test/pleroma/web/metadata/providers/restrict_indexing_test.exs similarity index 86% rename from test/web/metadata/restrict_indexing_test.exs rename to test/pleroma/web/metadata/providers/restrict_indexing_test.exs index 6b3a65372..282d132c8 100644 --- a/test/web/metadata/restrict_indexing_test.exs +++ b/test/pleroma/web/metadata/providers/restrict_indexing_test.exs @@ -14,13 +14,13 @@ test "for remote user" do test "for local user" do assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ - user: %Pleroma.User{local: true, discoverable: true} + user: %Pleroma.User{local: true, is_discoverable: true} }) == [] end test "for local user when discoverable is false" do assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ - user: %Pleroma.User{local: true, discoverable: false} + user: %Pleroma.User{local: true, is_discoverable: false} }) == [{:meta, [name: "robots", content: "noindex, noarchive"], []}] end end diff --git a/test/web/metadata/twitter_card_test.exs b/test/pleroma/web/metadata/providers/twitter_card_test.exs similarity index 100% rename from test/web/metadata/twitter_card_test.exs rename to test/pleroma/web/metadata/providers/twitter_card_test.exs diff --git a/test/web/metadata/utils_test.exs b/test/pleroma/web/metadata/utils_test.exs similarity index 100% rename from test/web/metadata/utils_test.exs rename to test/pleroma/web/metadata/utils_test.exs diff --git a/test/web/metadata/metadata_test.exs b/test/pleroma/web/metadata_test.exs similarity index 90% rename from test/web/metadata/metadata_test.exs rename to test/pleroma/web/metadata_test.exs index ca6cbe67f..8fb946540 100644 --- a/test/web/metadata/metadata_test.exs +++ b/test/pleroma/web/metadata_test.exs @@ -16,14 +16,14 @@ test "for remote user" do end test "for local user" do - user = insert(:user, discoverable: false) + user = insert(:user, is_discoverable: false) assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~ "" end test "for local user set to discoverable" do - user = insert(:user, discoverable: true) + user = insert(:user, is_discoverable: true) refute Pleroma.Web.Metadata.build_tags(%{user: user}) =~ "" @@ -33,14 +33,14 @@ test "for local user set to discoverable" do describe "no metadata for private instances" do test "for local user set to discoverable" do clear_config([:instance, :public], false) - user = insert(:user, bio: "This is my secret fedi account bio", discoverable: true) + user = insert(:user, bio: "This is my secret fedi account bio", is_discoverable: true) assert "" = Pleroma.Web.Metadata.build_tags(%{user: user}) end test "search exclusion metadata is included" do clear_config([:instance, :public], false) - user = insert(:user, bio: "This is my secret fedi account bio", discoverable: false) + user = insert(:user, bio: "This is my secret fedi account bio", is_discoverable: false) assert ~s() == Pleroma.Web.Metadata.build_tags(%{user: user}) diff --git a/test/web/mongooseim/mongoose_im_controller_test.exs b/test/pleroma/web/mongoose_im_controller_test.exs similarity index 97% rename from test/web/mongooseim/mongoose_im_controller_test.exs rename to test/pleroma/web/mongoose_im_controller_test.exs index 5176cde84..e3a8aa3d8 100644 --- a/test/web/mongooseim/mongoose_im_controller_test.exs +++ b/test/pleroma/web/mongoose_im_controller_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.MongooseIMController do +defmodule Pleroma.Web.MongooseIMControllerTest do use Pleroma.Web.ConnCase import Pleroma.Factory diff --git a/test/web/node_info_test.exs b/test/pleroma/web/node_info_test.exs similarity index 100% rename from test/web/node_info_test.exs rename to test/pleroma/web/node_info_test.exs diff --git a/test/web/oauth/app_test.exs b/test/pleroma/web/o_auth/app_test.exs similarity index 100% rename from test/web/oauth/app_test.exs rename to test/pleroma/web/o_auth/app_test.exs diff --git a/test/web/oauth/authorization_test.exs b/test/pleroma/web/o_auth/authorization_test.exs similarity index 100% rename from test/web/oauth/authorization_test.exs rename to test/pleroma/web/o_auth/authorization_test.exs diff --git a/test/web/oauth/ldap_authorization_test.exs b/test/pleroma/web/o_auth/ldap_authorization_test.exs similarity index 100% rename from test/web/oauth/ldap_authorization_test.exs rename to test/pleroma/web/o_auth/ldap_authorization_test.exs diff --git a/test/web/oauth/mfa_controller_test.exs b/test/pleroma/web/o_auth/mfa_controller_test.exs similarity index 100% rename from test/web/oauth/mfa_controller_test.exs rename to test/pleroma/web/o_auth/mfa_controller_test.exs diff --git a/test/web/oauth/oauth_controller_test.exs b/test/pleroma/web/o_auth/o_auth_controller_test.exs similarity index 99% rename from test/web/oauth/oauth_controller_test.exs rename to test/pleroma/web/o_auth/o_auth_controller_test.exs index 1200126b8..a00df8cc7 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/pleroma/web/o_auth/o_auth_controller_test.exs @@ -77,7 +77,7 @@ test "GET /oauth/prepare_request encodes parameters as `state` and redirects", % } ) - assert response = html_response(conn, 302) + assert html_response(conn, 302) redirect_query = URI.parse(redirected_to(conn)).query assert %{"state" => state_param} = URI.decode_query(redirect_query) @@ -119,7 +119,7 @@ test "with user-bound registration, GET /oauth//callback redirects to } ) - assert response = html_response(conn, 302) + assert html_response(conn, 302) assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ end @@ -182,7 +182,7 @@ test "on authentication error, GET /oauth//callback redirects to `redi } ) - assert response = html_response(conn, 302) + assert html_response(conn, 302) assert redirected_to(conn) == app.redirect_uris assert get_flash(conn, :error) == "Failed to authenticate: (error description)." end @@ -238,7 +238,7 @@ test "with valid params, POST /oauth/register?op=register redirects to `redirect } ) - assert response = html_response(conn, 302) + assert html_response(conn, 302) assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ end @@ -268,7 +268,7 @@ test "with unlisted `redirect_uri`, POST /oauth/register?op=register results in } ) - assert response = html_response(conn, 401) + assert html_response(conn, 401) end test "with invalid params, POST /oauth/register?op=register renders registration_details page", @@ -336,7 +336,7 @@ test "with valid params, POST /oauth/register?op=connect redirects to `redirect_ } ) - assert response = html_response(conn, 302) + assert html_response(conn, 302) assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ end @@ -367,7 +367,7 @@ test "with unlisted `redirect_uri`, POST /oauth/register?op=connect results in H } ) - assert response = html_response(conn, 401) + assert html_response(conn, 401) end test "with invalid params, POST /oauth/register?op=connect renders registration_details page", diff --git a/test/web/oauth/token/utils_test.exs b/test/pleroma/web/o_auth/token/utils_test.exs similarity index 100% rename from test/web/oauth/token/utils_test.exs rename to test/pleroma/web/o_auth/token/utils_test.exs diff --git a/test/web/oauth/token_test.exs b/test/pleroma/web/o_auth/token_test.exs similarity index 100% rename from test/web/oauth/token_test.exs rename to test/pleroma/web/o_auth/token_test.exs diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/pleroma/web/o_status/o_status_controller_test.exs similarity index 94% rename from test/web/ostatus/ostatus_controller_test.exs rename to test/pleroma/web/o_status/o_status_controller_test.exs index ee498f4b5..65b2c22db 100644 --- a/test/web/ostatus/ostatus_controller_test.exs +++ b/test/pleroma/web/o_status/o_status_controller_test.exs @@ -7,7 +7,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do import Pleroma.Factory - alias Pleroma.Config alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub @@ -21,7 +20,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do :ok end - setup do: clear_config([:instance, :federating], true) + setup do: clear_config([:static_fe, :enabled], false) describe "Mastodon compatibility routes" do setup %{conn: conn} do @@ -215,15 +214,16 @@ test "404s a non-existing notice", %{conn: conn} do assert response(conn, 404) end - test "it requires authentication if instance is NOT federating", %{ + test "does not require authentication on non-federating instances", %{ conn: conn } do - user = insert(:user) + clear_config([:instance, :federating], false) note_activity = insert(:note_activity) - conn = put_req_header(conn, "accept", "text/html") - - ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}", user) + conn + |> put_req_header("accept", "text/html") + |> get("/notice/#{note_activity.id}") + |> response(200) end end @@ -325,14 +325,16 @@ test "404s when attachment isn't audio or video", %{conn: conn} do |> response(404) end - test "it requires authentication if instance is NOT federating", %{ + test "does not require authentication on non-federating instances", %{ conn: conn, note_activity: note_activity } do - user = insert(:user) - conn = put_req_header(conn, "accept", "text/html") + clear_config([:instance, :federating], false) - ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}/embed_player", user) + conn + |> put_req_header("accept", "text/html") + |> get("/notice/#{note_activity.id}/embed_player") + |> response(200) end end end diff --git a/test/web/pleroma_api/controllers/account_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs similarity index 100% rename from test/web/pleroma_api/controllers/account_controller_test.exs rename to test/pleroma/web/pleroma_api/controllers/account_controller_test.exs diff --git a/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs new file mode 100644 index 000000000..f1941f6dd --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/backup_controller_test.exs @@ -0,0 +1,85 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.BackupControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.User.Backup + alias Pleroma.Web.PleromaAPI.BackupView + + setup do + clear_config([Pleroma.Upload, :uploader]) + clear_config([Backup, :limit_days]) + oauth_access(["read:accounts"]) + end + + test "GET /api/v1/pleroma/backups", %{user: user, conn: conn} do + assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id}}} = Backup.create(user) + + backup = Backup.get(backup_id) + + response = + conn + |> get("/api/v1/pleroma/backups") + |> json_response_and_validate_schema(:ok) + + assert [ + %{ + "content_type" => "application/zip", + "url" => url, + "file_size" => 0, + "processed" => false, + "inserted_at" => _ + } + ] = response + + assert url == BackupView.download_url(backup) + + Pleroma.Tests.ObanHelpers.perform_all() + + assert [ + %{ + "url" => ^url, + "processed" => true + } + ] = + conn + |> get("/api/v1/pleroma/backups") + |> json_response_and_validate_schema(:ok) + end + + test "POST /api/v1/pleroma/backups", %{user: _user, conn: conn} do + assert [ + %{ + "content_type" => "application/zip", + "url" => url, + "file_size" => 0, + "processed" => false, + "inserted_at" => _ + } + ] = + conn + |> post("/api/v1/pleroma/backups") + |> json_response_and_validate_schema(:ok) + + Pleroma.Tests.ObanHelpers.perform_all() + + assert [ + %{ + "url" => ^url, + "processed" => true + } + ] = + conn + |> get("/api/v1/pleroma/backups") + |> json_response_and_validate_schema(:ok) + + days = Pleroma.Config.get([Backup, :limit_days]) + + assert %{"error" => "Last export was less than #{days} days ago"} == + conn + |> post("/api/v1/pleroma/backups") + |> json_response_and_validate_schema(400) + end +end diff --git a/test/web/pleroma_api/controllers/chat_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs similarity index 85% rename from test/web/pleroma_api/controllers/chat_controller_test.exs rename to test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs index 44a78a738..c1e6a8cc5 100644 --- a/test/web/pleroma_api/controllers/chat_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase alias Pleroma.Chat alias Pleroma.Chat.MessageReference @@ -82,11 +82,13 @@ test "it posts a message to the chat", %{conn: conn, user: user} do result = conn |> put_req_header("content-type", "application/json") + |> put_req_header("idempotency-key", "123") |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "Hallo!!"}) |> json_response_and_validate_schema(200) assert result["content"] == "Hallo!!" assert result["chat_id"] == chat.id |> to_string() + assert result["idempotency_key"] == "123" end test "it fails if there is no content", %{conn: conn, user: user} do @@ -105,7 +107,7 @@ test "it fails if there is no content", %{conn: conn, user: user} do test "it works with an attachment", %{conn: conn, user: user} do file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -201,17 +203,39 @@ test "it paginates", %{conn: conn, user: user} do chat = Chat.get(user.id, recipient.ap_id) - result = - conn - |> get("/api/v1/pleroma/chats/#{chat.id}/messages") - |> json_response_and_validate_schema(200) + response = get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages") + result = json_response_and_validate_schema(response, 200) + + [next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ") + api_endpoint = "/api/v1/pleroma/chats/" + + assert String.match?( + next, + ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$) + ) + + assert String.match?( + prev, + ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&min_id=.*; rel=\"prev\"$) + ) assert length(result) == 20 - result = - conn - |> get("/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}") - |> json_response_and_validate_schema(200) + response = + get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}") + + result = json_response_and_validate_schema(response, 200) + [next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ") + + assert String.match?( + next, + ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$) + ) + + assert String.match?( + prev, + ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*&min_id=.*; rel=\"prev\"$) + ) assert length(result) == 10 end @@ -240,12 +264,10 @@ test "it returns the messages for a given chat", %{conn: conn, user: user} do assert length(result) == 3 # Trying to get the chat of a different user - result = - conn - |> assign(:user, other_user) - |> get("/api/v1/pleroma/chats/#{chat.id}/messages") - - assert result |> json_response(404) + conn + |> assign(:user, other_user) + |> get("/api/v1/pleroma/chats/#{chat.id}/messages") + |> json_response_and_validate_schema(404) end end @@ -321,6 +343,35 @@ test "it does not return chats with users you blocked", %{conn: conn, user: user 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 _ -> recipient = insert(:user) diff --git a/test/web/pleroma_api/controllers/conversation_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs similarity index 96% rename from test/web/pleroma_api/controllers/conversation_controller_test.exs rename to test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs index e6d0b3e37..f2feeaaef 100644 --- a/test/web/pleroma_api/controllers/conversation_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs @@ -121,7 +121,7 @@ test "POST /api/v1/pleroma/conversations/read" do [participation2, participation1] = Participation.for_user(other_user) assert Participation.get(participation2.id).read == false assert Participation.get(participation1.id).read == false - assert User.get_cached_by_id(other_user.id).unread_conversation_count == 2 + assert Participation.unread_count(other_user) == 2 [%{"unread" => false}, %{"unread" => false}] = conn @@ -131,6 +131,6 @@ test "POST /api/v1/pleroma/conversations/read" do [participation2, participation1] = Participation.for_user(other_user) assert Participation.get(participation2.id).read == true assert Participation.get(participation1.id).read == true - assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0 + assert Participation.unread_count(other_user) == 0 end end diff --git a/test/web/pleroma_api/controllers/emoji_file_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_file_controller_test.exs similarity index 88% rename from test/web/pleroma_api/controllers/emoji_file_controller_test.exs rename to test/pleroma/web/pleroma_api/controllers/emoji_file_controller_test.exs index 39b4e1dac..82de86ee3 100644 --- a/test/web/pleroma_api/controllers/emoji_file_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/emoji_file_controller_test.exs @@ -29,7 +29,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do {:ok, %{admin_conn: admin_conn}} end - describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/:name/files" do + describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/files?name=:name" do setup do pack_file = "#{@emoji_path}/test_pack/pack.json" original_content = File.read!(pack_file) @@ -56,7 +56,7 @@ test "upload zip file with emojies", %{admin_conn: admin_conn} do resp = admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ file: %Plug.Upload{ content_type: "application/zip", filename: "emojis.zip", @@ -83,7 +83,7 @@ test "upload zip file with emojies", %{admin_conn: admin_conn} do test "create shortcode exists", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank", filename: "dir/blank.png", file: %Plug.Upload{ @@ -101,7 +101,7 @@ test "don't rewrite old emoji", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank3", filename: "dir/blank.png", file: %Plug.Upload{ @@ -119,7 +119,7 @@ test "don't rewrite old emoji", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank", new_shortcode: "blank2", new_filename: "dir_2/blank_3.png" @@ -135,7 +135,7 @@ test "rewrite old emoji with force option", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank3", filename: "dir/blank.png", file: %Plug.Upload{ @@ -153,7 +153,7 @@ test "rewrite old emoji with force option", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank3", new_shortcode: "blank4", new_filename: "dir_2/blank_3.png", @@ -171,7 +171,7 @@ test "rewrite old emoji with force option", %{admin_conn: admin_conn} do test "with empty filename", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank2", filename: "", file: %Plug.Upload{ @@ -187,7 +187,7 @@ test "with empty filename", %{admin_conn: admin_conn} do test "add file with not loaded pack", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/not_loaded/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=not_loaded", %{ shortcode: "blank3", filename: "dir/blank.png", file: %Plug.Upload{ @@ -202,7 +202,7 @@ test "add file with not loaded pack", %{admin_conn: admin_conn} do test "remove file with not loaded pack", %{admin_conn: admin_conn} do assert admin_conn - |> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=blank3") + |> delete("/api/pleroma/emoji/packs/files?name=not_loaded&shortcode=blank3") |> json_response_and_validate_schema(:not_found) == %{ "error" => "pack \"not_loaded\" is not found" } @@ -210,7 +210,7 @@ test "remove file with not loaded pack", %{admin_conn: admin_conn} do test "remove file with empty shortcode", %{admin_conn: admin_conn} do assert admin_conn - |> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=") + |> delete("/api/pleroma/emoji/packs/files?name=not_loaded&shortcode=") |> json_response_and_validate_schema(:not_found) == %{ "error" => "pack \"not_loaded\" is not found" } @@ -219,7 +219,7 @@ test "remove file with empty shortcode", %{admin_conn: admin_conn} do test "update file with not loaded pack", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/not_loaded/files", %{ + |> patch("/api/pleroma/emoji/packs/files?name=not_loaded", %{ shortcode: "blank4", new_shortcode: "blank3", new_filename: "dir_2/blank_3.png" @@ -232,7 +232,7 @@ test "update file with not loaded pack", %{admin_conn: admin_conn} do test "new with shortcode as file with update", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank4", filename: "dir/blank.png", file: %Plug.Upload{ @@ -250,7 +250,7 @@ test "new with shortcode as file with update", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank4", new_shortcode: "blank3", new_filename: "dir_2/blank_3.png" @@ -265,7 +265,7 @@ test "new with shortcode as file with update", %{admin_conn: admin_conn} do assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") assert admin_conn - |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") + |> delete("/api/pleroma/emoji/packs/files?name=test_pack&shortcode=blank3") |> json_response_and_validate_schema(200) == %{ "blank" => "blank.png", "blank2" => "blank2.png" @@ -287,7 +287,7 @@ test "new with shortcode from url", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank_url", file: "https://test-blank/blank_url.png" }) @@ -307,7 +307,7 @@ test "new without shortcode", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ file: %Plug.Upload{ filename: "shortcode.png", path: "#{Pleroma.Config.get([:instance, :static_dir])}/add/shortcode.png" @@ -322,7 +322,7 @@ test "new without shortcode", %{admin_conn: admin_conn} do test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do assert admin_conn - |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") + |> delete("/api/pleroma/emoji/packs/files?name=test_pack&shortcode=blank3") |> json_response_and_validate_schema(:bad_request) == %{ "error" => "Emoji \"blank3\" does not exist" } @@ -331,7 +331,7 @@ test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do test "update non existing emoji", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank3", new_shortcode: "blank4", new_filename: "dir_2/blank_3.png" @@ -347,7 +347,7 @@ test "update with empty shortcode", %{admin_conn: admin_conn} do } = admin_conn |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank", new_filename: "dir_2/blank_3.png" }) diff --git a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs similarity index 82% rename from test/web/pleroma_api/controllers/emoji_pack_controller_test.exs rename to test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs index a34df2c18..3445f0ca0 100644 --- a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs @@ -37,11 +37,11 @@ test "GET /api/pleroma/emoji/packs when :public: false", %{conn: conn} do test "GET /api/pleroma/emoji/packs", %{conn: conn} do resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) - assert resp["count"] == 3 + assert resp["count"] == 4 assert resp["packs"] |> Map.keys() - |> length() == 3 + |> length() == 4 shared = resp["packs"]["test_pack"] assert shared["files"] == %{"blank" => "blank.png", "blank2" => "blank2.png"} @@ -58,7 +58,7 @@ test "GET /api/pleroma/emoji/packs", %{conn: conn} do |> get("/api/pleroma/emoji/packs?page_size=1") |> json_response_and_validate_schema(200) - assert resp["count"] == 3 + assert resp["count"] == 4 packs = Map.keys(resp["packs"]) @@ -71,7 +71,7 @@ test "GET /api/pleroma/emoji/packs", %{conn: conn} do |> get("/api/pleroma/emoji/packs?page_size=1&page=2") |> json_response_and_validate_schema(200) - assert resp["count"] == 3 + assert resp["count"] == 4 packs = Map.keys(resp["packs"]) assert length(packs) == 1 [pack2] = packs @@ -81,18 +81,28 @@ test "GET /api/pleroma/emoji/packs", %{conn: conn} do |> get("/api/pleroma/emoji/packs?page_size=1&page=3") |> json_response_and_validate_schema(200) - assert resp["count"] == 3 + assert resp["count"] == 4 packs = Map.keys(resp["packs"]) assert length(packs) == 1 [pack3] = packs - assert [pack1, pack2, pack3] |> Enum.uniq() |> length() == 3 + + resp = + conn + |> get("/api/pleroma/emoji/packs?page_size=1&page=4") + |> json_response_and_validate_schema(200) + + assert resp["count"] == 4 + packs = Map.keys(resp["packs"]) + assert length(packs) == 1 + [pack4] = packs + assert [pack1, pack2, pack3, pack4] |> Enum.uniq() |> length() == 4 end describe "GET /api/pleroma/emoji/packs/remote" do test "shareable instance", %{admin_conn: admin_conn, conn: conn} do resp = conn - |> get("/api/pleroma/emoji/packs") + |> get("/api/pleroma/emoji/packs?page=2&page_size=1") |> json_response_and_validate_schema(200) mock(fn @@ -102,12 +112,12 @@ test "shareable instance", %{admin_conn: admin_conn, conn: conn} do %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> json(%{metadata: %{features: ["shareable_emoji_packs"]}}) - %{method: :get, url: "https://example.com/api/pleroma/emoji/packs"} -> + %{method: :get, url: "https://example.com/api/pleroma/emoji/packs?page=2&page_size=1"} -> json(resp) end) assert admin_conn - |> get("/api/pleroma/emoji/packs/remote?url=https://example.com") + |> get("/api/pleroma/emoji/packs/remote?url=https://example.com&page=2&page_size=1") |> json_response_and_validate_schema(200) == resp end @@ -128,11 +138,11 @@ test "non shareable instance", %{admin_conn: admin_conn} do end end - describe "GET /api/pleroma/emoji/packs/:name/archive" do + describe "GET /api/pleroma/emoji/packs/archive?name=:name" do test "download shared pack", %{conn: conn} do resp = conn - |> get("/api/pleroma/emoji/packs/test_pack/archive") + |> get("/api/pleroma/emoji/packs/archive?name=test_pack") |> response(200) {:ok, arch} = :zip.unzip(resp, [:memory]) @@ -143,7 +153,7 @@ test "download shared pack", %{conn: conn} do test "non existing pack", %{conn: conn} do assert conn - |> get("/api/pleroma/emoji/packs/test_pack_for_import/archive") + |> get("/api/pleroma/emoji/packs/archive?name=test_pack_for_import") |> json_response_and_validate_schema(:not_found) == %{ "error" => "Pack test_pack_for_import does not exist" } @@ -151,7 +161,7 @@ test "non existing pack", %{conn: conn} do test "non downloadable pack", %{conn: conn} do assert conn - |> get("/api/pleroma/emoji/packs/test_pack_nonshared/archive") + |> get("/api/pleroma/emoji/packs/archive?name=test_pack_nonshared") |> json_response_and_validate_schema(:forbidden) == %{ "error" => "Pack test_pack_nonshared cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing" @@ -173,28 +183,28 @@ test "shared pack from remote and non shared from fallback-src", %{ %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/test_pack" + url: "https://example.com/api/pleroma/emoji/pack?name=test_pack" } -> conn - |> get("/api/pleroma/emoji/packs/test_pack") + |> get("/api/pleroma/emoji/pack?name=test_pack") |> json_response_and_validate_schema(200) |> json() %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/test_pack/archive" + url: "https://example.com/api/pleroma/emoji/packs/archive?name=test_pack" } -> conn - |> get("/api/pleroma/emoji/packs/test_pack/archive") + |> get("/api/pleroma/emoji/packs/archive?name=test_pack") |> response(200) |> text() %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/test_pack_nonshared" + url: "https://example.com/api/pleroma/emoji/pack?name=test_pack_nonshared" } -> conn - |> get("/api/pleroma/emoji/packs/test_pack_nonshared") + |> get("/api/pleroma/emoji/pack?name=test_pack_nonshared") |> json_response_and_validate_schema(200) |> json() @@ -218,7 +228,7 @@ test "shared pack from remote and non shared from fallback-src", %{ assert File.exists?("#{@emoji_path}/test_pack2/blank.png") assert admin_conn - |> delete("/api/pleroma/emoji/packs/test_pack2") + |> delete("/api/pleroma/emoji/pack?name=test_pack2") |> json_response_and_validate_schema(200) == "ok" refute File.exists?("#{@emoji_path}/test_pack2") @@ -239,7 +249,7 @@ test "shared pack from remote and non shared from fallback-src", %{ assert File.exists?("#{@emoji_path}/test_pack_nonshared2/blank.png") assert admin_conn - |> delete("/api/pleroma/emoji/packs/test_pack_nonshared2") + |> delete("/api/pleroma/emoji/pack?name=test_pack_nonshared2") |> json_response_and_validate_schema(200) == "ok" refute File.exists?("#{@emoji_path}/test_pack_nonshared2") @@ -279,14 +289,14 @@ test "checksum fail", %{admin_conn: admin_conn} do %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/pack_bad_sha" + url: "https://example.com/api/pleroma/emoji/pack?name=pack_bad_sha" } -> {:ok, pack} = Pleroma.Emoji.Pack.load_pack("pack_bad_sha") %Tesla.Env{status: 200, body: Jason.encode!(pack)} %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/pack_bad_sha/archive" + url: "https://example.com/api/pleroma/emoji/packs/archive?name=pack_bad_sha" } -> %Tesla.Env{ status: 200, @@ -316,7 +326,7 @@ test "other error", %{admin_conn: admin_conn} do %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/test_pack" + url: "https://example.com/api/pleroma/emoji/pack?name=test_pack" } -> {:ok, pack} = Pleroma.Emoji.Pack.load_pack("test_pack") %Tesla.Env{status: 200, body: Jason.encode!(pack)} @@ -336,7 +346,7 @@ test "other error", %{admin_conn: admin_conn} do end end - describe "PATCH /api/pleroma/emoji/packs/:name" do + describe "PATCH /api/pleroma/emoji/pack?name=:name" do setup do pack_file = "#{@emoji_path}/test_pack/pack.json" original_content = File.read!(pack_file) @@ -358,7 +368,9 @@ test "other error", %{admin_conn: admin_conn} do test "for a pack without a fallback source", ctx do assert ctx[:admin_conn] |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack", %{"metadata" => ctx[:new_data]}) + |> patch("/api/pleroma/emoji/pack?name=test_pack", %{ + "metadata" => ctx[:new_data] + }) |> json_response_and_validate_schema(200) == ctx[:new_data] assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data] @@ -384,7 +396,7 @@ test "for a pack with a fallback source", ctx do assert ctx[:admin_conn] |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack", %{metadata: new_data}) + |> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data}) |> json_response_and_validate_schema(200) == new_data_with_sha assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha @@ -404,17 +416,17 @@ test "when the fallback source doesn't have all the files", ctx do assert ctx[:admin_conn] |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack", %{metadata: new_data}) + |> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data}) |> json_response_and_validate_schema(:bad_request) == %{ "error" => "The fallback archive does not have all files specified in pack.json" } end end - describe "POST/DELETE /api/pleroma/emoji/packs/:name" do + describe "POST/DELETE /api/pleroma/emoji/pack?name=:name" do test "creating and deleting a pack", %{admin_conn: admin_conn} do assert admin_conn - |> post("/api/pleroma/emoji/packs/test_created") + |> post("/api/pleroma/emoji/pack?name=test_created") |> json_response_and_validate_schema(200) == "ok" assert File.exists?("#{@emoji_path}/test_created/pack.json") @@ -426,7 +438,7 @@ test "creating and deleting a pack", %{admin_conn: admin_conn} do } assert admin_conn - |> delete("/api/pleroma/emoji/packs/test_created") + |> delete("/api/pleroma/emoji/pack?name=test_created") |> json_response_and_validate_schema(200) == "ok" refute File.exists?("#{@emoji_path}/test_created/pack.json") @@ -439,7 +451,7 @@ test "if pack exists", %{admin_conn: admin_conn} do File.write!(Path.join(path, "pack.json"), pack_file) assert admin_conn - |> post("/api/pleroma/emoji/packs/test_created") + |> post("/api/pleroma/emoji/pack?name=test_created") |> json_response_and_validate_schema(:conflict) == %{ "error" => "A pack named \"test_created\" already exists" } @@ -449,7 +461,7 @@ test "if pack exists", %{admin_conn: admin_conn} do test "with empty name", %{admin_conn: admin_conn} do assert admin_conn - |> post("/api/pleroma/emoji/packs/ ") + |> post("/api/pleroma/emoji/pack?name= ") |> json_response_and_validate_schema(:bad_request) == %{ "error" => "pack name cannot be empty" } @@ -458,7 +470,7 @@ test "with empty name", %{admin_conn: admin_conn} do test "deleting nonexisting pack", %{admin_conn: admin_conn} do assert admin_conn - |> delete("/api/pleroma/emoji/packs/non_existing") + |> delete("/api/pleroma/emoji/pack?name=non_existing") |> json_response_and_validate_schema(:not_found) == %{ "error" => "Pack non_existing does not exist" } @@ -466,7 +478,7 @@ test "deleting nonexisting pack", %{admin_conn: admin_conn} do test "deleting with empty name", %{admin_conn: admin_conn} do assert admin_conn - |> delete("/api/pleroma/emoji/packs/ ") + |> delete("/api/pleroma/emoji/pack?name= ") |> json_response_and_validate_schema(:bad_request) == %{ "error" => "pack name cannot be empty" } @@ -514,7 +526,7 @@ test "filesystem import", %{admin_conn: admin_conn, conn: conn} do } end - describe "GET /api/pleroma/emoji/packs/:name" do + describe "GET /api/pleroma/emoji/pack?name=:name" do test "shows pack.json", %{conn: conn} do assert %{ "files" => files, @@ -529,7 +541,7 @@ test "shows pack.json", %{conn: conn} do } } = conn - |> get("/api/pleroma/emoji/packs/test_pack") + |> get("/api/pleroma/emoji/pack?name=test_pack") |> json_response_and_validate_schema(200) assert files == %{"blank" => "blank.png", "blank2" => "blank2.png"} @@ -539,7 +551,7 @@ test "shows pack.json", %{conn: conn} do "files_count" => 2 } = conn - |> get("/api/pleroma/emoji/packs/test_pack?page_size=1") + |> get("/api/pleroma/emoji/pack?name=test_pack&page_size=1") |> json_response_and_validate_schema(200) assert files |> Map.keys() |> length() == 1 @@ -549,15 +561,33 @@ test "shows pack.json", %{conn: conn} do "files_count" => 2 } = conn - |> get("/api/pleroma/emoji/packs/test_pack?page_size=1&page=2") + |> get("/api/pleroma/emoji/pack?name=test_pack&page_size=1&page=2") |> json_response_and_validate_schema(200) assert files |> Map.keys() |> length() == 1 end + test "for pack name with special chars", %{conn: conn} do + assert %{ + "files" => _files, + "files_count" => 1, + "pack" => %{ + "can-download" => true, + "description" => "Test description", + "download-sha256" => _, + "homepage" => "https://pleroma.social", + "license" => "Test license", + "share-files" => true + } + } = + conn + |> get("/api/pleroma/emoji/pack?name=blobs.gg") + |> json_response_and_validate_schema(200) + end + test "non existing pack", %{conn: conn} do assert conn - |> get("/api/pleroma/emoji/packs/non_existing") + |> get("/api/pleroma/emoji/pack?name=non_existing") |> json_response_and_validate_schema(:not_found) == %{ "error" => "Pack non_existing does not exist" } @@ -565,7 +595,7 @@ test "non existing pack", %{conn: conn} do test "error name", %{conn: conn} do assert conn - |> get("/api/pleroma/emoji/packs/ ") + |> get("/api/pleroma/emoji/pack?name= ") |> json_response_and_validate_schema(:bad_request) == %{ "error" => "pack name cannot be empty" } diff --git a/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs similarity index 100% rename from test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs rename to test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs diff --git a/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs new file mode 100644 index 000000000..13491ed9c --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/instances_controller_test.exs @@ -0,0 +1,38 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaApi.InstancesControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Instances + + setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1) + + setup do + constant = "http://consistently-unreachable.name/" + eventual = "http://eventually-unreachable.com/path" + + {:ok, %Pleroma.Instances.Instance{unreachable_since: constant_unreachable}} = + Instances.set_consistently_unreachable(constant) + + _eventual_unrechable = Instances.set_unreachable(eventual) + + %{constant_unreachable: constant_unreachable, constant: constant} + end + + test "GET /api/v1/pleroma/federation_status", %{ + conn: conn, + constant_unreachable: constant_unreachable, + constant: constant + } do + constant_host = URI.parse(constant).host + + assert conn + |> put_req_header("content-type", "application/json") + |> get("/api/v1/pleroma/federation_status") + |> json_response_and_validate_schema(200) == %{ + "unreachable" => %{constant_host => to_string(constant_unreachable)} + } + end +end diff --git a/test/web/pleroma_api/controllers/mascot_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs similarity index 92% rename from test/web/pleroma_api/controllers/mascot_controller_test.exs rename to test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs index e2ead6e15..289119d45 100644 --- a/test/web/pleroma_api/controllers/mascot_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs @@ -24,7 +24,7 @@ test "mascot upload" do assert json_response_and_validate_schema(ret_conn, 415) file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -34,7 +34,7 @@ test "mascot upload" do |> put_req_header("content-type", "multipart/form-data") |> put("/api/v1/pleroma/mascot", %{"file" => file}) - assert %{"id" => _, "type" => image} = json_response_and_validate_schema(conn, 200) + assert %{"id" => _, "type" => _image} = json_response_and_validate_schema(conn, 200) end test "mascot retrieving" do @@ -48,7 +48,7 @@ test "mascot retrieving" do # When a user sets their mascot, we should get that back file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } diff --git a/test/web/pleroma_api/controllers/notification_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/notification_controller_test.exs similarity index 100% rename from test/web/pleroma_api/controllers/notification_controller_test.exs rename to test/pleroma/web/pleroma_api/controllers/notification_controller_test.exs diff --git a/test/web/pleroma_api/controllers/scrobble_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs similarity index 100% rename from test/web/pleroma_api/controllers/scrobble_controller_test.exs rename to test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs diff --git a/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs similarity index 98% rename from test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs rename to test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs index d23d08a00..22988c881 100644 --- a/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationControllerTest do use Pleroma.Web.ConnCase diff --git a/test/web/pleroma_api/controllers/user_import_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs similarity index 99% rename from test/web/pleroma_api/controllers/user_import_controller_test.exs rename to test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs index 433c97e81..68723de71 100644 --- a/test/web/pleroma_api/controllers/user_import_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs @@ -6,7 +6,6 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do use Pleroma.Web.ConnCase use Oban.Testing, repo: Pleroma.Repo - alias Pleroma.Config alias Pleroma.Tests.ObanHelpers import Pleroma.Factory diff --git a/test/web/pleroma_api/views/chat/message_reference_view_test.exs b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs similarity index 89% rename from test/web/pleroma_api/views/chat/message_reference_view_test.exs rename to test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs index 40dbae3cd..ae8257870 100644 --- a/test/web/pleroma_api/views/chat/message_reference_view_test.exs +++ b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceViewTest do +defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do use Pleroma.DataCase alias Pleroma.Chat @@ -19,13 +19,15 @@ test "it displays a chat message" do recipient = insert(:user) file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) - {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "kippis :firefox:") + + {:ok, activity} = + CommonAPI.post_chat_message(user, recipient, "kippis :firefox:", idempotency_key: "123") chat = Chat.get(user.id, recipient.ap_id) @@ -42,6 +44,7 @@ test "it displays a chat message" do assert chat_message[:created_at] assert chat_message[:unread] == false assert match?([%{shortcode: "firefox"}], chat_message[:emojis]) + assert chat_message[:idempotency_key] == "123" clear_config([:rich_media, :enabled], true) diff --git a/test/web/pleroma_api/views/chat_view_test.exs b/test/pleroma/web/pleroma_api/views/chat_view_test.exs similarity index 100% rename from test/web/pleroma_api/views/chat_view_test.exs rename to test/pleroma/web/pleroma_api/views/chat_view_test.exs diff --git a/test/web/pleroma_api/views/scrobble_view_test.exs b/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs similarity index 91% rename from test/web/pleroma_api/views/scrobble_view_test.exs rename to test/pleroma/web/pleroma_api/views/scrobble_view_test.exs index 6bdb56509..0f43cbdc3 100644 --- a/test/web/pleroma_api/views/scrobble_view_test.exs +++ b/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.PleromaAPI.StatusViewTest do +defmodule Pleroma.Web.PleromaAPI.ScrobbleViewTest do use Pleroma.DataCase alias Pleroma.Web.PleromaAPI.ScrobbleView diff --git a/test/plugs/admin_secret_authentication_plug_test.exs b/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs similarity index 88% rename from test/plugs/admin_secret_authentication_plug_test.exs rename to test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs index 14094eda8..33394722a 100644 --- a/test/plugs/admin_secret_authentication_plug_test.exs +++ b/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs @@ -2,16 +2,16 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do +defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlugTest do use Pleroma.Web.ConnCase import Mock import Pleroma.Factory - alias Pleroma.Plugs.AdminSecretAuthenticationPlug - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.PlugHelper - alias Pleroma.Plugs.RateLimiter + alias Pleroma.Web.Plugs.AdminSecretAuthenticationPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.PlugHelper + alias Pleroma.Web.Plugs.RateLimiter test "does nothing if a user is assigned", %{conn: conn} do user = insert(:user) diff --git a/test/plugs/authentication_plug_test.exs b/test/pleroma/web/plugs/authentication_plug_test.exs similarity index 93% rename from test/plugs/authentication_plug_test.exs rename to test/pleroma/web/plugs/authentication_plug_test.exs index 777ae15ae..af39352e2 100644 --- a/test/plugs/authentication_plug_test.exs +++ b/test/pleroma/web/plugs/authentication_plug_test.exs @@ -2,13 +2,13 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.AuthenticationPlugTest do +defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.AuthenticationPlug - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.PlugHelper alias Pleroma.User + alias Pleroma.Web.Plugs.AuthenticationPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.PlugHelper import ExUnit.CaptureLog import Pleroma.Factory @@ -118,7 +118,7 @@ test "it returns false when hash invalid" do "psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1" assert capture_log(fn -> - refute Pleroma.Plugs.AuthenticationPlug.checkpw("password", hash) + refute AuthenticationPlug.checkpw("password", hash) end) =~ "[error] Password hash not recognized" end end diff --git a/test/plugs/basic_auth_decoder_plug_test.exs b/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs similarity index 89% rename from test/plugs/basic_auth_decoder_plug_test.exs rename to test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs index a6063d4f6..2d6af228c 100644 --- a/test/plugs/basic_auth_decoder_plug_test.exs +++ b/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs @@ -2,10 +2,10 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.BasicAuthDecoderPlugTest do +defmodule Pleroma.Web.Plugs.BasicAuthDecoderPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.BasicAuthDecoderPlug + alias Pleroma.Web.Plugs.BasicAuthDecoderPlug defp basic_auth_enc(username, password) do "Basic " <> Base.encode64("#{username}:#{password}") diff --git a/test/plugs/cache_control_test.exs b/test/pleroma/web/plugs/cache_control_test.exs similarity index 92% rename from test/plugs/cache_control_test.exs rename to test/pleroma/web/plugs/cache_control_test.exs index 6b567e81d..fcf3d2be8 100644 --- a/test/plugs/cache_control_test.exs +++ b/test/pleroma/web/plugs/cache_control_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.CacheControlTest do +defmodule Pleroma.Web.Plugs.CacheControlTest do use Pleroma.Web.ConnCase alias Plug.Conn diff --git a/test/plugs/cache_test.exs b/test/pleroma/web/plugs/cache_test.exs similarity index 98% rename from test/plugs/cache_test.exs rename to test/pleroma/web/plugs/cache_test.exs index 8b231c881..93a66f5d3 100644 --- a/test/plugs/cache_test.exs +++ b/test/pleroma/web/plugs/cache_test.exs @@ -2,11 +2,11 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.CacheTest do +defmodule Pleroma.Web.Plugs.CacheTest do use ExUnit.Case, async: true use Plug.Test - alias Pleroma.Plugs.Cache + alias Pleroma.Web.Plugs.Cache @miss_resp {200, [ diff --git a/test/plugs/ensure_authenticated_plug_test.exs b/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs similarity index 96% rename from test/plugs/ensure_authenticated_plug_test.exs rename to test/pleroma/web/plugs/ensure_authenticated_plug_test.exs index a0667c5e0..92ff19282 100644 --- a/test/plugs/ensure_authenticated_plug_test.exs +++ b/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs @@ -2,11 +2,11 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.EnsureAuthenticatedPlugTest do +defmodule Pleroma.Web.Plugs.EnsureAuthenticatedPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.EnsureAuthenticatedPlug alias Pleroma.User + alias Pleroma.Web.Plugs.EnsureAuthenticatedPlug describe "without :if_func / :unless_func options" do test "it halts if user is NOT assigned", %{conn: conn} do diff --git a/test/plugs/ensure_public_or_authenticated_plug_test.exs b/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs similarity index 89% rename from test/plugs/ensure_public_or_authenticated_plug_test.exs rename to test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs index fc2934369..211443a55 100644 --- a/test/plugs/ensure_public_or_authenticated_plug_test.exs +++ b/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs @@ -2,12 +2,12 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlugTest do +defmodule Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlugTest do use Pleroma.Web.ConnCase, async: true alias Pleroma.Config - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.User + alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug setup do: clear_config([:instance, :public]) diff --git a/test/plugs/ensure_user_key_plug_test.exs b/test/pleroma/web/plugs/ensure_user_key_plug_test.exs similarity index 86% rename from test/plugs/ensure_user_key_plug_test.exs rename to test/pleroma/web/plugs/ensure_user_key_plug_test.exs index 633c05447..f912ef755 100644 --- a/test/plugs/ensure_user_key_plug_test.exs +++ b/test/pleroma/web/plugs/ensure_user_key_plug_test.exs @@ -2,10 +2,10 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.EnsureUserKeyPlugTest do +defmodule Pleroma.Web.Plugs.EnsureUserKeyPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.EnsureUserKeyPlug + alias Pleroma.Web.Plugs.EnsureUserKeyPlug test "if the conn has a user key set, it does nothing", %{conn: conn} do conn = diff --git a/test/web/plugs/federating_plug_test.exs b/test/pleroma/web/plugs/federating_plug_test.exs similarity index 80% rename from test/web/plugs/federating_plug_test.exs rename to test/pleroma/web/plugs/federating_plug_test.exs index 2f8aadadc..a4652f6c5 100644 --- a/test/web/plugs/federating_plug_test.exs +++ b/test/pleroma/web/plugs/federating_plug_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.FederatingPlugTest do +defmodule Pleroma.Web.Plugs.FederatingPlugTest do use Pleroma.Web.ConnCase setup do: clear_config([:instance, :federating]) @@ -12,7 +12,7 @@ test "returns and halt the conn when federating is disabled" do conn = build_conn() - |> Pleroma.Web.FederatingPlug.call(%{}) + |> Pleroma.Web.Plugs.FederatingPlug.call(%{}) assert conn.status == 404 assert conn.halted @@ -23,7 +23,7 @@ test "does nothing when federating is enabled" do conn = build_conn() - |> Pleroma.Web.FederatingPlug.call(%{}) + |> Pleroma.Web.Plugs.FederatingPlug.call(%{}) refute conn.status refute conn.halted diff --git a/test/plugs/frontend_static_test.exs b/test/pleroma/web/plugs/frontend_static_plug_test.exs similarity index 62% rename from test/plugs/frontend_static_test.exs rename to test/pleroma/web/plugs/frontend_static_plug_test.exs index 6f4923048..8b7b022fc 100644 --- a/test/plugs/frontend_static_test.exs +++ b/test/pleroma/web/plugs/frontend_static_plug_test.exs @@ -2,9 +2,9 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.FrontendStaticPlugTest do - alias Pleroma.Plugs.FrontendStatic +defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do use Pleroma.Web.ConnCase + import Mock @dir "test/tmp/instance_static" @@ -21,7 +21,7 @@ test "init will give a static plug config + the frontend type" do at: "/admin", frontend_type: :admin ] - |> FrontendStatic.init() + |> Pleroma.Web.Plugs.FrontendStatic.init() assert opts[:at] == ["admin"] assert opts[:frontend_type] == :admin @@ -54,4 +54,24 @@ test "overrides existing static files for the `pleroma/admin` path", %{conn: con index = get(conn, "/pleroma/admin/") assert html_response(index, 200) == "from frontend plug" end + + test "exclude invalid path", %{conn: conn} do + name = "pleroma-fe" + ref = "dist" + clear_config([:media_proxy, :enabled], true) + clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") + clear_config([:frontends, :primary], %{"name" => name, "ref" => ref}) + path = "#{@dir}/frontends/#{name}/#{ref}" + + File.mkdir_p!("#{path}/proxy/rr/ss") + File.write!("#{path}/proxy/rr/ss/Ek7w8WPVcAApOvN.jpg:large", "FB image") + + url = + Pleroma.Web.MediaProxy.encode_url("https://pbs.twimg.com/media/Ek7w8WPVcAApOvN.jpg:large") + + with_mock Pleroma.ReverseProxy, + call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do + assert %Plug.Conn{status: :success} = get(conn, url) + end + end end diff --git a/test/plugs/http_security_plug_test.exs b/test/pleroma/web/plugs/http_security_plug_test.exs similarity index 99% rename from test/plugs/http_security_plug_test.exs rename to test/pleroma/web/plugs/http_security_plug_test.exs index 2297e3dac..df2b5ebb3 100644 --- a/test/plugs/http_security_plug_test.exs +++ b/test/pleroma/web/plugs/http_security_plug_test.exs @@ -5,7 +5,6 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do use Pleroma.Web.ConnCase - alias Pleroma.Config alias Plug.Conn describe "http security enabled" do diff --git a/test/plugs/http_signature_plug_test.exs b/test/pleroma/web/plugs/http_signature_plug_test.exs similarity index 100% rename from test/plugs/http_signature_plug_test.exs rename to test/pleroma/web/plugs/http_signature_plug_test.exs diff --git a/test/plugs/idempotency_plug_test.exs b/test/pleroma/web/plugs/idempotency_plug_test.exs similarity index 97% rename from test/plugs/idempotency_plug_test.exs rename to test/pleroma/web/plugs/idempotency_plug_test.exs index 21fa0fbcf..4a7835993 100644 --- a/test/plugs/idempotency_plug_test.exs +++ b/test/pleroma/web/plugs/idempotency_plug_test.exs @@ -2,11 +2,11 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.IdempotencyPlugTest do +defmodule Pleroma.Web.Plugs.IdempotencyPlugTest do use ExUnit.Case, async: true use Plug.Test - alias Pleroma.Plugs.IdempotencyPlug + alias Pleroma.Web.Plugs.IdempotencyPlug alias Plug.Conn test "returns result from cache" do diff --git a/test/plugs/instance_static_test.exs b/test/pleroma/web/plugs/instance_static_test.exs similarity index 97% rename from test/plugs/instance_static_test.exs rename to test/pleroma/web/plugs/instance_static_test.exs index d42ba817e..5b30011d3 100644 --- a/test/plugs/instance_static_test.exs +++ b/test/pleroma/web/plugs/instance_static_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.InstanceStaticPlugTest do +defmodule Pleroma.Web.Plugs.InstanceStaticTest do use Pleroma.Web.ConnCase @dir "test/tmp/instance_static" diff --git a/test/plugs/legacy_authentication_plug_test.exs b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs similarity index 90% rename from test/plugs/legacy_authentication_plug_test.exs rename to test/pleroma/web/plugs/legacy_authentication_plug_test.exs index 3b8c07627..2016a31a8 100644 --- a/test/plugs/legacy_authentication_plug_test.exs +++ b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs @@ -2,15 +2,15 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.LegacyAuthenticationPlugTest do +defmodule Pleroma.Web.Plugs.LegacyAuthenticationPlugTest do use Pleroma.Web.ConnCase import Pleroma.Factory - alias Pleroma.Plugs.LegacyAuthenticationPlug - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.PlugHelper alias Pleroma.User + alias Pleroma.Web.Plugs.LegacyAuthenticationPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.PlugHelper setup do user = diff --git a/test/plugs/mapped_identity_to_signature_plug_test.exs b/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs similarity index 100% rename from test/plugs/mapped_identity_to_signature_plug_test.exs rename to test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs diff --git a/test/plugs/oauth_plug_test.exs b/test/pleroma/web/plugs/o_auth_plug_test.exs similarity index 96% rename from test/plugs/oauth_plug_test.exs rename to test/pleroma/web/plugs/o_auth_plug_test.exs index 9d39d3153..b9d722f76 100644 --- a/test/plugs/oauth_plug_test.exs +++ b/test/pleroma/web/plugs/o_auth_plug_test.exs @@ -2,10 +2,10 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.OAuthPlugTest do +defmodule Pleroma.Web.Plugs.OAuthPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.OAuthPlug + alias Pleroma.Web.Plugs.OAuthPlug import Pleroma.Factory @session_opts [ diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs similarity index 98% rename from test/plugs/oauth_scopes_plug_test.exs rename to test/pleroma/web/plugs/o_auth_scopes_plug_test.exs index 334316043..982a70bf9 100644 --- a/test/plugs/oauth_scopes_plug_test.exs +++ b/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs @@ -2,11 +2,11 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.OAuthScopesPlugTest do +defmodule Pleroma.Web.Plugs.OAuthScopesPlugTest do use Pleroma.Web.ConnCase - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo + alias Pleroma.Web.Plugs.OAuthScopesPlug import Mock import Pleroma.Factory diff --git a/test/web/plugs/plug_test.exs b/test/pleroma/web/plugs/plug_helper_test.exs similarity index 94% rename from test/web/plugs/plug_test.exs rename to test/pleroma/web/plugs/plug_helper_test.exs index 943e484e7..670d699f0 100644 --- a/test/web/plugs/plug_test.exs +++ b/test/pleroma/web/plugs/plug_helper_test.exs @@ -2,12 +2,12 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.PlugTest do +defmodule Pleroma.Web.Plugs.PlugHelperTest do @moduledoc "Tests for the functionality added via `use Pleroma.Web, :plug`" - alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug - alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug - alias Pleroma.Plugs.PlugHelper + alias Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug + alias Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug + alias Pleroma.Web.Plugs.PlugHelper import Mock diff --git a/test/plugs/rate_limiter_test.exs b/test/pleroma/web/plugs/rate_limiter_test.exs similarity index 98% rename from test/plugs/rate_limiter_test.exs rename to test/pleroma/web/plugs/rate_limiter_test.exs index 4d3d694f4..249c78b37 100644 --- a/test/plugs/rate_limiter_test.exs +++ b/test/pleroma/web/plugs/rate_limiter_test.exs @@ -2,12 +2,12 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.RateLimiterTest do +defmodule Pleroma.Web.Plugs.RateLimiterTest do use Pleroma.Web.ConnCase alias Phoenix.ConnTest alias Pleroma.Config - alias Pleroma.Plugs.RateLimiter + alias Pleroma.Web.Plugs.RateLimiter alias Plug.Conn import Pleroma.Factory @@ -19,7 +19,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do describe "config" do @limiter_name :test_init - setup do: clear_config([Pleroma.Plugs.RemoteIp, :enabled]) + 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}) diff --git a/test/plugs/remote_ip_test.exs b/test/pleroma/web/plugs/remote_ip_test.exs similarity index 57% rename from test/plugs/remote_ip_test.exs rename to test/pleroma/web/plugs/remote_ip_test.exs index 752ab32e7..0bdb4c168 100644 --- a/test/plugs/remote_ip_test.exs +++ b/test/pleroma/web/plugs/remote_ip_test.exs @@ -2,14 +2,28 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.RemoteIpTest do - use ExUnit.Case, async: true +defmodule Pleroma.Web.Plugs.RemoteIpTest do + use ExUnit.Case use Plug.Test - alias Pleroma.Plugs.RemoteIp + alias Pleroma.Web.Plugs.RemoteIp - import Pleroma.Tests.Helpers, only: [clear_config: 1, clear_config: 2] - setup do: clear_config(RemoteIp) + import Pleroma.Tests.Helpers, only: [clear_config: 2] + + setup do: + clear_config(RemoteIp, + enabled: true, + headers: ["x-forwarded-for"], + proxies: [], + reserved: [ + "127.0.0.0/8", + "::1/128", + "fc00::/7", + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16" + ] + ) test "disabled" do Pleroma.Config.put(RemoteIp, enabled: false) @@ -25,8 +39,6 @@ test "disabled" do end test "enabled" do - Pleroma.Config.put(RemoteIp, enabled: true) - conn = conn(:get, "/") |> put_req_header("x-forwarded-for", "1.1.1.1") @@ -54,8 +66,6 @@ test "custom headers" do end test "custom proxies" do - Pleroma.Config.put(RemoteIp, enabled: true) - conn = conn(:get, "/") |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2") @@ -72,4 +82,27 @@ test "custom proxies" do assert conn.remote_ip == {1, 1, 1, 1} end + + test "proxies set without CIDR format" do + Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.1"]) + + conn = + conn(:get, "/") + |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1") + |> RemoteIp.call(nil) + + assert conn.remote_ip == {1, 1, 1, 1} + 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"]) + + conn = + conn(:get, "/") + |> put_req_header("x-forwarded-for", "10.0.0.3, 1.1.1.1") + |> RemoteIp.call(nil) + + assert conn.remote_ip == {1, 1, 1, 1} + end end diff --git a/test/plugs/session_authentication_plug_test.exs b/test/pleroma/web/plugs/session_authentication_plug_test.exs similarity index 92% rename from test/plugs/session_authentication_plug_test.exs rename to test/pleroma/web/plugs/session_authentication_plug_test.exs index 0949ecfed..2b4d5bc0c 100644 --- a/test/plugs/session_authentication_plug_test.exs +++ b/test/pleroma/web/plugs/session_authentication_plug_test.exs @@ -2,11 +2,11 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.SessionAuthenticationPlugTest do +defmodule Pleroma.Web.Plugs.SessionAuthenticationPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.SessionAuthenticationPlug alias Pleroma.User + alias Pleroma.Web.Plugs.SessionAuthenticationPlug setup %{conn: conn} do session_opts = [ diff --git a/test/plugs/set_format_plug_test.exs b/test/pleroma/web/plugs/set_format_plug_test.exs similarity index 89% rename from test/plugs/set_format_plug_test.exs rename to test/pleroma/web/plugs/set_format_plug_test.exs index 7a1dfe9bf..e95d751fa 100644 --- a/test/plugs/set_format_plug_test.exs +++ b/test/pleroma/web/plugs/set_format_plug_test.exs @@ -2,11 +2,11 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.SetFormatPlugTest do +defmodule Pleroma.Web.Plugs.SetFormatPlugTest do use ExUnit.Case, async: true use Plug.Test - alias Pleroma.Plugs.SetFormatPlug + alias Pleroma.Web.Plugs.SetFormatPlug test "set format from params" do conn = diff --git a/test/plugs/set_locale_plug_test.exs b/test/pleroma/web/plugs/set_locale_plug_test.exs similarity index 92% rename from test/plugs/set_locale_plug_test.exs rename to test/pleroma/web/plugs/set_locale_plug_test.exs index 7114b1557..773f48a5b 100644 --- a/test/plugs/set_locale_plug_test.exs +++ b/test/pleroma/web/plugs/set_locale_plug_test.exs @@ -2,11 +2,11 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.SetLocalePlugTest do +defmodule Pleroma.Web.Plugs.SetLocalePlugTest do use ExUnit.Case, async: true use Plug.Test - alias Pleroma.Plugs.SetLocalePlug + alias Pleroma.Web.Plugs.SetLocalePlug alias Plug.Conn test "default locale is `en`" do diff --git a/test/plugs/set_user_session_id_plug_test.exs b/test/pleroma/web/plugs/set_user_session_id_plug_test.exs similarity index 90% rename from test/plugs/set_user_session_id_plug_test.exs rename to test/pleroma/web/plugs/set_user_session_id_plug_test.exs index 7f1a1e98b..a89b5628f 100644 --- a/test/plugs/set_user_session_id_plug_test.exs +++ b/test/pleroma/web/plugs/set_user_session_id_plug_test.exs @@ -2,11 +2,11 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.SetUserSessionIdPlugTest do +defmodule Pleroma.Web.Plugs.SetUserSessionIdPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.SetUserSessionIdPlug alias Pleroma.User + alias Pleroma.Web.Plugs.SetUserSessionIdPlug setup %{conn: conn} do session_opts = [ diff --git a/test/plugs/uploaded_media_plug_test.exs b/test/pleroma/web/plugs/uploaded_media_plug_test.exs similarity index 93% rename from test/plugs/uploaded_media_plug_test.exs rename to test/pleroma/web/plugs/uploaded_media_plug_test.exs index 20b13dfac..7c8313121 100644 --- a/test/plugs/uploaded_media_plug_test.exs +++ b/test/pleroma/web/plugs/uploaded_media_plug_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.UploadedMediaPlugTest do +defmodule Pleroma.Web.Plugs.UploadedMediaPlugTest do use Pleroma.Web.ConnCase alias Pleroma.Upload @@ -11,7 +11,7 @@ defp upload_file(context) do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), filename: "nice_tf.jpg" } diff --git a/test/plugs/user_enabled_plug_test.exs b/test/pleroma/web/plugs/user_enabled_plug_test.exs similarity index 93% rename from test/plugs/user_enabled_plug_test.exs rename to test/pleroma/web/plugs/user_enabled_plug_test.exs index b219d8abf..71c56f03a 100644 --- a/test/plugs/user_enabled_plug_test.exs +++ b/test/pleroma/web/plugs/user_enabled_plug_test.exs @@ -2,10 +2,10 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.UserEnabledPlugTest do +defmodule Pleroma.Web.Plugs.UserEnabledPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.UserEnabledPlug + alias Pleroma.Web.Plugs.UserEnabledPlug import Pleroma.Factory setup do: clear_config([:instance, :account_activation_required]) diff --git a/test/plugs/user_fetcher_plug_test.exs b/test/pleroma/web/plugs/user_fetcher_plug_test.exs similarity index 89% rename from test/plugs/user_fetcher_plug_test.exs rename to test/pleroma/web/plugs/user_fetcher_plug_test.exs index 0496f14dd..b4f875d2d 100644 --- a/test/plugs/user_fetcher_plug_test.exs +++ b/test/pleroma/web/plugs/user_fetcher_plug_test.exs @@ -2,10 +2,10 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.UserFetcherPlugTest do +defmodule Pleroma.Web.Plugs.UserFetcherPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.UserFetcherPlug + alias Pleroma.Web.Plugs.UserFetcherPlug import Pleroma.Factory setup do diff --git a/test/plugs/user_is_admin_plug_test.exs b/test/pleroma/web/plugs/user_is_admin_plug_test.exs similarity index 89% rename from test/plugs/user_is_admin_plug_test.exs rename to test/pleroma/web/plugs/user_is_admin_plug_test.exs index 8bc00e444..b550568c1 100644 --- a/test/plugs/user_is_admin_plug_test.exs +++ b/test/pleroma/web/plugs/user_is_admin_plug_test.exs @@ -2,10 +2,10 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Plugs.UserIsAdminPlugTest do +defmodule Pleroma.Web.Plugs.UserIsAdminPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.UserIsAdminPlug + alias Pleroma.Web.Plugs.UserIsAdminPlug import Pleroma.Factory test "accepts a user that is an admin" do diff --git a/test/web/preload/instance_test.exs b/test/pleroma/web/preload/providers/instance_test.exs similarity index 85% rename from test/web/preload/instance_test.exs rename to test/pleroma/web/preload/providers/instance_test.exs index a46f28312..8493f2a94 100644 --- a/test/web/preload/instance_test.exs +++ b/test/pleroma/web/preload/providers/instance_test.exs @@ -45,4 +45,12 @@ test "it renders the node_info", %{"/nodeinfo/2.0.json" => nodeinfo} do assert metadata.private == false assert metadata.suggestions == %{enabled: false} end + + test "it renders the frontend configurations", %{ + "/api/pleroma/frontend_configurations" => fe_configs + } do + assert %{ + pleroma_fe: %{background: "/images/city.jpg", logo: "/static/logo.png"} + } = fe_configs + end end diff --git a/test/web/preload/timeline_test.exs b/test/pleroma/web/preload/providers/timeline_test.exs similarity index 100% rename from test/web/preload/timeline_test.exs rename to test/pleroma/web/preload/providers/timeline_test.exs diff --git a/test/web/preload/user_test.exs b/test/pleroma/web/preload/providers/user_test.exs similarity index 100% rename from test/web/preload/user_test.exs rename to test/pleroma/web/preload/providers/user_test.exs diff --git a/test/web/push/impl_test.exs b/test/pleroma/web/push/impl_test.exs similarity index 92% rename from test/web/push/impl_test.exs rename to test/pleroma/web/push/impl_test.exs index c7c17e156..7d8cc999a 100644 --- a/test/web/push/impl_test.exs +++ b/test/pleroma/web/push/impl_test.exs @@ -5,6 +5,8 @@ defmodule Pleroma.Web.Push.ImplTest do use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Notification alias Pleroma.Object alias Pleroma.User @@ -12,10 +14,6 @@ defmodule Pleroma.Web.Push.ImplTest do alias Pleroma.Web.CommonAPI alias Pleroma.Web.Push.Impl alias Pleroma.Web.Push.Subscription - alias Pleroma.Web.WebPushHttpClientMock - - import Mock - import Pleroma.Factory setup do Tesla.Mock.mock(fn @@ -80,22 +78,6 @@ test "successful message sending" do assert Impl.push_message(@message, @sub, @api_key, %Subscription{}) == :ok end - test_with_mock "uses WebPushHttpClientMock as an HTTP client", WebPushHttpClientMock, - post: fn _, _, _ -> {:ok, %{status_code: 200}} end do - Impl.push_message(@message, @sub, @api_key, %Subscription{}) - assert_called(WebPushHttpClientMock.post("https://example.com/example/1234", :_, :_)) - end - - test_with_mock "uses Pleroma.HTTP as an HTTP client", Pleroma.HTTP, - post: fn _, _, _ -> {:ok, %{status_code: 200}} end do - client = Application.get_env(:web_push_encryption, :http_client) - on_exit(fn -> Application.put_env(:web_push_encryption, :http_client, client) end) - Application.put_env(:web_push_encryption, :http_client, Pleroma.HTTP) - - Impl.push_message(@message, @sub, @api_key, %Subscription{}) - assert_called(Pleroma.HTTP.post("https://example.com/example/1234", :_, :_)) - end - @tag capture_log: true test "fail message sending" do assert Impl.push_message( @@ -237,7 +219,7 @@ test "builds content for chat messages with no content" do recipient = insert(:user) file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } diff --git a/test/web/rel_me_test.exs b/test/pleroma/web/rel_me_test.exs similarity index 100% rename from test/web/rel_me_test.exs rename to test/pleroma/web/rel_me_test.exs diff --git a/test/web/rich_media/helpers_test.exs b/test/pleroma/web/rich_media/helpers_test.exs similarity index 74% rename from test/web/rich_media/helpers_test.exs rename to test/pleroma/web/rich_media/helpers_test.exs index 8264a9c41..4c9ee77d0 100644 --- a/test/web/rich_media/helpers_test.exs +++ b/test/pleroma/web/rich_media/helpers_test.exs @@ -6,7 +6,6 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do use Pleroma.DataCase alias Pleroma.Config - alias Pleroma.Object alias Pleroma.Web.CommonAPI alias Pleroma.Web.RichMedia.Helpers @@ -64,41 +63,6 @@ test "crawls valid, complete URLs" do Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end - test "refuses to crawl URLs from posts marked sensitive" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "http://example.com/ogp", - sensitive: true - }) - - %Object{} = object = Object.normalize(activity) - - assert object.data["sensitive"] - - Config.put([:rich_media, :enabled], true) - - assert %{} = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) - end - - test "refuses to crawl URLs from posts tagged NSFW" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "http://example.com/ogp #nsfw" - }) - - %Object{} = object = Object.normalize(activity) - - assert object.data["sensitive"] - - Config.put([:rich_media, :enabled], true) - - assert %{} = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) - end - test "refuses to crawl URLs of private network from posts" do user = insert(:user) diff --git a/test/web/rich_media/aws_signed_url_test.exs b/test/pleroma/web/rich_media/parser/ttl/aws_signed_url_test.exs similarity index 97% rename from test/web/rich_media/aws_signed_url_test.exs rename to test/pleroma/web/rich_media/parser/ttl/aws_signed_url_test.exs index 1ceae1a31..2f17bebd7 100644 --- a/test/web/rich_media/aws_signed_url_test.exs +++ b/test/pleroma/web/rich_media/parser/ttl/aws_signed_url_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.RichMedia.TTL.AwsSignedUrlTest do +defmodule Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrlTest do use ExUnit.Case, async: true test "s3 signed url is parsed correct for expiration time" do diff --git a/test/web/rich_media/parser_test.exs b/test/pleroma/web/rich_media/parser_test.exs similarity index 100% rename from test/web/rich_media/parser_test.exs rename to test/pleroma/web/rich_media/parser_test.exs diff --git a/test/web/rich_media/parsers/twitter_card_test.exs b/test/pleroma/web/rich_media/parsers/twitter_card_test.exs similarity index 100% rename from test/web/rich_media/parsers/twitter_card_test.exs rename to test/pleroma/web/rich_media/parsers/twitter_card_test.exs diff --git a/test/web/static_fe/static_fe_controller_test.exs b/test/pleroma/web/static_fe/static_fe_controller_test.exs similarity index 79% rename from test/web/static_fe/static_fe_controller_test.exs rename to test/pleroma/web/static_fe/static_fe_controller_test.exs index 1598bf675..19506f1d8 100644 --- a/test/web/static_fe/static_fe_controller_test.exs +++ b/test/pleroma/web/static_fe/static_fe_controller_test.exs @@ -1,15 +1,17 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do use Pleroma.Web.ConnCase alias Pleroma.Activity - alias Pleroma.Config alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.CommonAPI import Pleroma.Factory setup_all do: clear_config([:static_fe, :enabled], true) - setup do: clear_config([:instance, :federating], true) setup %{conn: conn} do conn = put_req_header(conn, "accept", "text/html") @@ -70,8 +72,27 @@ test "pagination, page 2", %{conn: conn, user: user} do refute html =~ ">test29<" end - test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}", user) + test "does not require authentication on non-federating instances", %{ + conn: conn, + user: user + } do + clear_config([:instance, :federating], false) + + conn = get(conn, "/users/#{user.nickname}") + + assert html_response(conn, 200) =~ user.nickname + end + + test "returns 404 for local user with `restrict_unauthenticated/profiles/local` setting", %{ + conn: conn + } do + clear_config([:restrict_unauthenticated, :profiles, :local], true) + + local_user = insert(:user, local: true) + + conn + |> get("/users/#{local_user.nickname}") + |> html_response(404) end end @@ -183,10 +204,28 @@ test "302 for remote cached status", %{conn: conn, user: user} do assert html_response(conn, 302) =~ "redirected" end - test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do + test "does not require authentication on non-federating instances", %{ + conn: conn, + user: user + } do + clear_config([:instance, :federating], false) + {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) - ensure_federating_or_authenticated(conn, "/notice/#{activity.id}", user) + conn = get(conn, "/notice/#{activity.id}") + + assert html_response(conn, 200) =~ "testing a thing!" + end + + test "returns 404 for local public activity with `restrict_unauthenticated/activities/local` setting", + %{conn: conn, user: user} do + clear_config([:restrict_unauthenticated, :activities, :local], true) + + {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) + + conn + |> get("/notice/#{activity.id}") + |> html_response(404) end end end diff --git a/test/web/streamer/streamer_test.exs b/test/pleroma/web/streamer_test.exs similarity index 98% rename from test/web/streamer/streamer_test.exs rename to test/pleroma/web/streamer_test.exs index 185724a9f..0d89e01d0 100644 --- a/test/web/streamer/streamer_test.exs +++ b/test/pleroma/web/streamer_test.exs @@ -29,6 +29,14 @@ test "allows public" do assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil) end + test "allows instance streams" do + assert {:ok, "public:remote:lain.com"} = + Streamer.get_topic("public:remote", nil, nil, %{"instance" => "lain.com"}) + + assert {:ok, "public:remote:media:lain.com"} = + Streamer.get_topic("public:remote:media", nil, nil, %{"instance" => "lain.com"}) + end + test "allows hashtag streams" do assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, nil, %{"tag" => "cofe"}) end @@ -255,7 +263,9 @@ test "it sends chat messages to the 'user:pleroma_chat' stream", %{ } do other_user = insert(:user) - {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno") + {:ok, create_activity} = + CommonAPI.post_chat_message(other_user, user, "hey cirno", idempotency_key: "123") + object = Object.normalize(create_activity, false) chat = Chat.get(user.id, other_user.ap_id) cm_ref = MessageReference.for_chat_and_object(chat, object) diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/pleroma/web/twitter_api/controller_test.exs similarity index 100% rename from test/web/twitter_api/twitter_api_controller_test.exs rename to test/pleroma/web/twitter_api/controller_test.exs diff --git a/test/web/twitter_api/password_controller_test.exs b/test/pleroma/web/twitter_api/password_controller_test.exs similarity index 100% rename from test/web/twitter_api/password_controller_test.exs rename to test/pleroma/web/twitter_api/password_controller_test.exs diff --git a/test/web/twitter_api/remote_follow_controller_test.exs b/test/pleroma/web/twitter_api/remote_follow_controller_test.exs similarity index 99% rename from test/web/twitter_api/remote_follow_controller_test.exs rename to test/pleroma/web/twitter_api/remote_follow_controller_test.exs index 3852c7ce9..a3e784d13 100644 --- a/test/web/twitter_api/remote_follow_controller_test.exs +++ b/test/pleroma/web/twitter_api/remote_follow_controller_test.exs @@ -5,7 +5,6 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do use Pleroma.Web.ConnCase - alias Pleroma.Config alias Pleroma.MFA alias Pleroma.MFA.TOTP alias Pleroma.User diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/pleroma/web/twitter_api/twitter_api_test.exs similarity index 100% rename from test/web/twitter_api/twitter_api_test.exs rename to test/pleroma/web/twitter_api/twitter_api_test.exs diff --git a/test/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs similarity index 100% rename from test/web/twitter_api/util_controller_test.exs rename to test/pleroma/web/twitter_api/util_controller_test.exs diff --git a/test/web/uploader_controller_test.exs b/test/pleroma/web/uploader_controller_test.exs similarity index 100% rename from test/web/uploader_controller_test.exs rename to test/pleroma/web/uploader_controller_test.exs diff --git a/test/web/views/error_view_test.exs b/test/pleroma/web/views/error_view_test.exs similarity index 100% rename from test/web/views/error_view_test.exs rename to test/pleroma/web/views/error_view_test.exs diff --git a/test/web/web_finger/web_finger_controller_test.exs b/test/pleroma/web/web_finger/web_finger_controller_test.exs similarity index 100% rename from test/web/web_finger/web_finger_controller_test.exs rename to test/pleroma/web/web_finger/web_finger_controller_test.exs diff --git a/test/web/web_finger/web_finger_test.exs b/test/pleroma/web/web_finger_test.exs similarity index 100% rename from test/web/web_finger/web_finger_test.exs rename to test/pleroma/web/web_finger_test.exs diff --git a/test/workers/cron/digest_emails_worker_test.exs b/test/pleroma/workers/cron/digest_emails_worker_test.exs similarity index 100% rename from test/workers/cron/digest_emails_worker_test.exs rename to test/pleroma/workers/cron/digest_emails_worker_test.exs diff --git a/test/workers/cron/new_users_digest_worker_test.exs b/test/pleroma/workers/cron/new_users_digest_worker_test.exs similarity index 100% rename from test/workers/cron/new_users_digest_worker_test.exs rename to test/pleroma/workers/cron/new_users_digest_worker_test.exs diff --git a/test/workers/purge_expired_activity_test.exs b/test/pleroma/workers/purge_expired_activity_test.exs similarity index 100% rename from test/workers/purge_expired_activity_test.exs rename to test/pleroma/workers/purge_expired_activity_test.exs diff --git a/test/workers/purge_expired_token_test.exs b/test/pleroma/workers/purge_expired_token_test.exs similarity index 100% rename from test/workers/purge_expired_token_test.exs rename to test/pleroma/workers/purge_expired_token_test.exs diff --git a/test/workers/scheduled_activity_worker_test.exs b/test/pleroma/workers/scheduled_activity_worker_test.exs similarity index 100% rename from test/workers/scheduled_activity_worker_test.exs rename to test/pleroma/workers/scheduled_activity_worker_test.exs diff --git a/test/xml_builder_test.exs b/test/pleroma/xml_builder_test.exs similarity index 100% rename from test/xml_builder_test.exs rename to test/pleroma/xml_builder_test.exs diff --git a/test/support/captcha_mock.ex b/test/support/captcha/mock.ex similarity index 100% rename from test/support/captcha_mock.ex rename to test/support/captcha/mock.ex diff --git a/test/support/channel_case.ex b/test/support/channel_case.ex index d63a0f06b..114184a9f 100644 --- a/test/support/channel_case.ex +++ b/test/support/channel_case.ex @@ -22,7 +22,7 @@ defmodule Pleroma.Web.ChannelCase do using do quote do # Import conveniences for testing with channels - use Phoenix.ChannelTest + import Phoenix.ChannelTest use Pleroma.Tests.Helpers # The default endpoint for testing diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 7ef681258..47cb65a80 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -22,7 +22,8 @@ defmodule Pleroma.Web.ConnCase do using do quote do # Import conveniences for testing with connections - use Phoenix.ConnTest + import Plug.Conn + import Phoenix.ConnTest use Pleroma.Tests.Helpers import Pleroma.Web.Router.Helpers @@ -111,28 +112,6 @@ defp json_response_and_validate_schema( defp json_response_and_validate_schema(conn, _status) do flunk("Response schema not found for #{conn.method} #{conn.request_path} #{conn.status}") end - - defp ensure_federating_or_authenticated(conn, url, user) do - initial_setting = Config.get([:instance, :federating]) - on_exit(fn -> Config.put([:instance, :federating], initial_setting) end) - - Config.put([:instance, :federating], false) - - conn - |> get(url) - |> response(403) - - conn - |> assign(:user, user) - |> get(url) - |> response(200) - - Config.put([:instance, :federating], true) - - conn - |> get(url) - |> response(200) - end end end diff --git a/test/support/factory.ex b/test/support/factory.ex index fb82be0c4..80b882ee4 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -31,7 +31,7 @@ def user_factory do nickname: sequence(:nickname, &"nick#{&1}"), password_hash: Pbkdf2.hash_pwd_salt("test"), bio: sequence(:bio, &"Tester Number #{&1}"), - discoverable: true, + is_discoverable: true, last_digest_emailed_at: NaiveDateTime.utc_now(), last_refreshed_at: NaiveDateTime.utc_now(), notification_settings: %Pleroma.User.NotificationSetting{}, diff --git a/test/support/oban_helpers.ex b/test/support/oban_helpers.ex index 9f90a821c..2468f66dc 100644 --- a/test/support/oban_helpers.ex +++ b/test/support/oban_helpers.ex @@ -7,6 +7,8 @@ defmodule Pleroma.Tests.ObanHelpers do Oban test helpers. """ + require Ecto.Query + alias Pleroma.Repo def wipe_all do @@ -15,6 +17,7 @@ def wipe_all do def perform_all do Oban.Job + |> Ecto.Query.where(state: "available") |> Repo.all() |> perform() end diff --git a/test/support/web_push_http_client_mock.ex b/test/support/web_push_http_client_mock.ex deleted file mode 100644 index 3cd12957d..000000000 --- a/test/support/web_push_http_client_mock.ex +++ /dev/null @@ -1,23 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.WebPushHttpClientMock do - def get(url, headers \\ [], options \\ []) do - { - res, - %Tesla.Env{status: status} - } = Pleroma.HTTP.request(:get, url, "", headers, options) - - {res, %{status_code: status}} - end - - def post(url, body, headers \\ [], options \\ []) do - { - res, - %Tesla.Env{status: status} - } = Pleroma.HTTP.request(:post, url, body, headers, options) - - {res, %{status_code: status}} - end -end diff --git a/test/tasks/email_test.exs b/test/tasks/email_test.exs deleted file mode 100644 index c3af7ef68..000000000 --- a/test/tasks/email_test.exs +++ /dev/null @@ -1,54 +0,0 @@ -defmodule Mix.Tasks.Pleroma.EmailTest do - use Pleroma.DataCase - - import Swoosh.TestAssertions - - alias Pleroma.Config - alias Pleroma.Tests.ObanHelpers - - setup_all do - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - end) - - :ok - end - - setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true) - - describe "pleroma.email test" do - test "Sends test email with no given address" do - mail_to = Config.get([:instance, :email]) - - :ok = Mix.Tasks.Pleroma.Email.run(["test"]) - - ObanHelpers.perform_all() - - assert_receive {:mix_shell, :info, [message]} - assert message =~ "Test email has been sent" - - assert_email_sent( - to: mail_to, - html_body: ~r/a test email was requested./i - ) - end - - test "Sends test email with given address" do - mail_to = "hewwo@example.com" - - :ok = Mix.Tasks.Pleroma.Email.run(["test", "--to", mail_to]) - - ObanHelpers.perform_all() - - assert_receive {:mix_shell, :info, [message]} - assert message =~ "Test email has been sent" - - assert_email_sent( - to: mail_to, - html_body: ~r/a test email was requested./i - ) - end - end -end diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs deleted file mode 100644 index cba6b43d3..000000000 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ /dev/null @@ -1,2034 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do - use Pleroma.Web.ConnCase - use Oban.Testing, repo: Pleroma.Repo - - import ExUnit.CaptureLog - import Mock - import Pleroma.Factory - import Swoosh.TestAssertions - - alias Pleroma.Activity - alias Pleroma.Config - alias Pleroma.HTML - alias Pleroma.MFA - alias Pleroma.ModerationLog - alias Pleroma.Repo - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web - alias Pleroma.Web.ActivityPub.Relay - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.MediaProxy - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - - :ok - end - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - test "with valid `admin_token` query parameter, skips OAuth scopes check" do - clear_config([:admin_token], "password123") - - user = insert(:user) - - conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123") - - 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}" - - 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 - - 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 - end - end - - describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) - - 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}" - - 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"]) - - 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 - end - end - - describe "DELETE /api/pleroma/admin/users" do - test "single user", %{admin: admin, conn: conn} do - clear_config([:instance, :federating], true) - - user = - insert(:user, - avatar: %{"url" => [%{"href" => "https://someurl"}]}, - banner: %{"url" => [%{"href" => "https://somebanner"}]}, - bio: "Hello world!", - name: "A guy" - ) - - # Create some activities to check they got deleted later - follower = insert(:user) - {:ok, _} = CommonAPI.post(user, %{status: "test"}) - {:ok, _, _, _} = CommonAPI.follow(user, follower) - {:ok, _, _, _} = CommonAPI.follow(follower, user) - user = Repo.get(User, user.id) - assert user.note_count == 1 - assert user.follower_count == 1 - assert user.following_count == 1 - refute user.deactivated - - with_mock Pleroma.Web.Federator, - publish: fn _ -> nil end, - perform: fn _, _ -> nil end do - conn = - conn - |> put_req_header("accept", "application/json") - |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}") - - ObanHelpers.perform_all() - - assert User.get_by_nickname(user.nickname).deactivated - - 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] - - user = Repo.get(User, user.id) - assert user.deactivated - - assert user.avatar == %{} - assert user.banner == %{} - assert user.note_count == 0 - assert user.follower_count == 0 - assert user.following_count == 0 - assert user.bio == "" - assert user.name == nil - - assert called(Pleroma.Web.Federator.publish(:_)) - end - end - - test "multiple users", %{admin: admin, conn: conn} do - user_one = insert(:user) - user_two = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> delete("/api/pleroma/admin/users", %{ - nicknames: [user_one.nickname, user_two.nickname] - }) - - 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 = - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users", %{ - "users" => [ - %{ - "nickname" => "lain", - "email" => "lain@example.org", - "password" => "test" - }, - %{ - "nickname" => "lain2", - "email" => "lain2@example.org", - "password" => "test" - } - ] - }) - - response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type")) - assert response == ["success", "success"] - - log_entry = Repo.one(ModerationLog) - - assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == [] - end - - test "Cannot create user with existing email", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users", %{ - "users" => [ - %{ - "nickname" => "lain", - "email" => user.email, - "password" => "test" - } - ] - }) - - assert json_response(conn, 409) == [ - %{ - "code" => 409, - "data" => %{ - "email" => user.email, - "nickname" => "lain" - }, - "error" => "email has already been taken", - "type" => "error" - } - ] - end - - test "Cannot create user with existing nickname", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users", %{ - "users" => [ - %{ - "nickname" => user.nickname, - "email" => "someuser@plerama.social", - "password" => "test" - } - ] - }) - - assert json_response(conn, 409) == [ - %{ - "code" => 409, - "data" => %{ - "email" => "someuser@plerama.social", - "nickname" => user.nickname - }, - "error" => "nickname has already been taken", - "type" => "error" - } - ] - end - - test "Multiple user creation works in transaction", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users", %{ - "users" => [ - %{ - "nickname" => "newuser", - "email" => "newuser@pleroma.social", - "password" => "test" - }, - %{ - "nickname" => "lain", - "email" => user.email, - "password" => "test" - } - ] - }) - - assert json_response(conn, 409) == [ - %{ - "code" => 409, - "data" => %{ - "email" => user.email, - "nickname" => "lain" - }, - "error" => "email has already been taken", - "type" => "error" - }, - %{ - "code" => 409, - "data" => %{ - "email" => "newuser@pleroma.social", - "nickname" => "newuser" - }, - "error" => "", - "type" => "error" - } - ] - - assert User.get_by_nickname("newuser") === nil - end - end - - describe "/api/pleroma/admin/users/:nickname" do - test "Show", %{conn: conn} do - user = insert(:user) - - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") - - expected = %{ - "deactivated" => false, - "id" => to_string(user.id), - "local" => true, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - - assert expected == json_response(conn, 200) - end - - test "when the user doesn't exist", %{conn: conn} do - user = build(:user) - - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") - - assert %{"error" => "Not found"} == json_response(conn, 404) - end - end - - describe "/api/pleroma/admin/users/follow" do - test "allows to force-follow another user", %{admin: admin, conn: conn} do - user = insert(:user) - follower = insert(:user) - - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users/follow", %{ - "follower" => follower.nickname, - "followed" => user.nickname - }) - - user = User.get_cached_by_id(user.id) - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, user) - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}" - end - end - - describe "/api/pleroma/admin/users/unfollow" do - test "allows to force-unfollow another user", %{admin: admin, conn: conn} do - user = insert(:user) - follower = insert(:user) - - User.follow(follower, user) - - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users/unfollow", %{ - "follower" => follower.nickname, - "followed" => user.nickname - }) - - user = User.get_cached_by_id(user.id) - follower = User.get_cached_by_id(follower.id) - - refute User.following?(follower, user) - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}" - end - end - - describe "PUT /api/pleroma/admin/users/tag" do - setup %{conn: conn} do - user1 = insert(:user, %{tags: ["x"]}) - user2 = insert(:user, %{tags: ["y"]}) - user3 = insert(:user, %{tags: ["unchanged"]}) - - conn = - conn - |> put_req_header("accept", "application/json") - |> put( - "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <> - "#{user2.nickname}&tags[]=foo&tags[]=bar" - ) - - %{conn: conn, user1: user1, user2: user2, user3: user3} - end - - test "it appends specified tags to users with specified nicknames", %{ - conn: conn, - admin: admin, - user1: user1, - user2: user2 - } do - assert empty_json_response(conn) - assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"] - assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"] - - log_entry = Repo.one(ModerationLog) - - users = - [user1.nickname, user2.nickname] - |> Enum.map(&"@#{&1}") - |> Enum.join(", ") - - tags = ["foo", "bar"] |> Enum.join(", ") - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} added tags: #{tags} to users: #{users}" - end - - test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do - assert empty_json_response(conn) - assert User.get_cached_by_id(user3.id).tags == ["unchanged"] - end - end - - describe "DELETE /api/pleroma/admin/users/tag" do - setup %{conn: conn} do - user1 = insert(:user, %{tags: ["x"]}) - user2 = insert(:user, %{tags: ["y", "z"]}) - user3 = insert(:user, %{tags: ["unchanged"]}) - - conn = - conn - |> put_req_header("accept", "application/json") - |> delete( - "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <> - "#{user2.nickname}&tags[]=x&tags[]=z" - ) - - %{conn: conn, user1: user1, user2: user2, user3: user3} - end - - test "it removes specified tags from users with specified nicknames", %{ - conn: conn, - admin: admin, - user1: user1, - user2: user2 - } do - assert empty_json_response(conn) - assert User.get_cached_by_id(user1.id).tags == [] - assert User.get_cached_by_id(user2.id).tags == ["y"] - - log_entry = Repo.one(ModerationLog) - - users = - [user1.nickname, user2.nickname] - |> Enum.map(&"@#{&1}") - |> Enum.join(", ") - - tags = ["x", "z"] |> Enum.join(", ") - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} removed tags: #{tags} from users: #{users}" - end - - test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do - assert empty_json_response(conn) - assert User.get_cached_by_id(user3.id).tags == ["unchanged"] - end - end - - describe "/api/pleroma/admin/users/:nickname/permission_group" do - test "GET is giving user_info", %{admin: admin, conn: conn} do - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/") - - assert json_response(conn, 200) == %{ - "is_admin" => true, - "is_moderator" => false - } - end - - test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin") - - assert json_response(conn, 200) == %{ - "is_admin" => true - } - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} made @#{user.nickname} admin" - end - - test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do - user_one = insert(:user) - user_two = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users/permission_group/admin", %{ - nicknames: [user_one.nickname, user_two.nickname] - }) - - assert json_response(conn, 200) == %{"is_admin" => true} - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin" - end - - test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do - user = insert(:user, is_admin: true) - - conn = - conn - |> put_req_header("accept", "application/json") - |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin") - - assert json_response(conn, 200) == %{"is_admin" => false} - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} revoked admin role from @#{user.nickname}" - end - - test "/:right DELETE, can remove from a permission group (multiple)", %{ - admin: admin, - conn: conn - } do - user_one = insert(:user, is_admin: true) - user_two = insert(:user, is_admin: true) - - conn = - conn - |> put_req_header("accept", "application/json") - |> delete("/api/pleroma/admin/users/permission_group/admin", %{ - nicknames: [user_one.nickname, user_two.nickname] - }) - - assert json_response(conn, 200) == %{"is_admin" => false} - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{ - user_two.nickname - }" - end - end - - test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset") - - resp = json_response(conn, 200) - - assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"]) - end - - 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") - - conn = get(conn, "/api/pleroma/admin/users?page=1") - - users = - [ - %{ - "deactivated" => admin.deactivated, - "id" => admin.id, - "nickname" => admin.nickname, - "roles" => %{"admin" => true, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(admin) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(admin.name || admin.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => admin.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - }, - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => false, - "tags" => ["foo", "bar"], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - }, - %{ - "deactivated" => user2.deactivated, - "id" => user2.id, - "nickname" => user2.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user2) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user2.name || user2.nickname), - "confirmation_pending" => false, - "approval_pending" => true, - "url" => user2.ap_id, - "registration_reason" => "I'm a chill dude", - "actor_type" => "Person" - } - ] - |> Enum.sort_by(& &1["nickname"]) - - assert json_response(conn, 200) == %{ - "count" => 3, - "page_size" => 50, - "users" => users - } - end - - test "pagination works correctly with service users", %{conn: conn} do - service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido") - - insert_list(25, :user) - - assert %{"count" => 26, "page_size" => 10, "users" => users1} = - conn - |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"}) - |> json_response(200) - - assert Enum.count(users1) == 10 - assert service1 not in users1 - - assert %{"count" => 26, "page_size" => 10, "users" => users2} = - conn - |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"}) - |> json_response(200) - - assert Enum.count(users2) == 10 - assert service1 not in users2 - - assert %{"count" => 26, "page_size" => 10, "users" => users3} = - conn - |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"}) - |> json_response(200) - - assert Enum.count(users3) == 6 - assert service1 not in users3 - end - - test "renders empty array for the second page", %{conn: conn} do - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?page=2") - - assert json_response(conn, 200) == %{ - "count" => 2, - "page_size" => 50, - "users" => [] - } - end - - test "regular search", %{conn: conn} do - user = insert(:user, nickname: "bob") - - conn = get(conn, "/api/pleroma/admin/users?query=bo") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "search by domain", %{conn: conn} do - user = insert(:user, nickname: "nickname@domain.com") - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?query=domain.com") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "search by full nickname", %{conn: conn} do - user = insert(:user, nickname: "nickname@domain.com") - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "search by display name", %{conn: conn} do - user = insert(:user, name: "Display name") - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?name=display") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "search by email", %{conn: conn} do - user = insert(:user, email: "email@example.com") - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?email=email@example.com") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "regular search with page size", %{conn: conn} do - user = insert(:user, nickname: "aalice") - user2 = insert(:user, nickname: "alice") - - conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1") - - assert json_response(conn1, 200) == %{ - "count" => 2, - "page_size" => 1, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - - conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2") - - assert json_response(conn2, 200) == %{ - "count" => 2, - "page_size" => 1, - "users" => [ - %{ - "deactivated" => user2.deactivated, - "id" => user2.id, - "nickname" => user2.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user2) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user2.name || user2.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user2.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "only local users" do - admin = insert(:user, is_admin: true, nickname: "john") - token = insert(:oauth_admin_token, user: admin) - user = insert(:user, nickname: "bob") - - insert(:user, nickname: "bobb", local: false) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - |> get("/api/pleroma/admin/users?query=bo&filters=local") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "only local users with no query", %{conn: conn, admin: old_admin} do - admin = insert(:user, is_admin: true, nickname: "john") - user = insert(:user, nickname: "bob") - - insert(:user, nickname: "bobb", local: false) - - conn = get(conn, "/api/pleroma/admin/users?filters=local") - - users = - [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - }, - %{ - "deactivated" => admin.deactivated, - "id" => admin.id, - "nickname" => admin.nickname, - "roles" => %{"admin" => true, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(admin) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(admin.name || admin.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => admin.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - }, - %{ - "deactivated" => false, - "id" => old_admin.id, - "local" => true, - "nickname" => old_admin.nickname, - "roles" => %{"admin" => true, "moderator" => false}, - "tags" => [], - "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => old_admin.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - |> Enum.sort_by(& &1["nickname"]) - - assert json_response(conn, 200) == %{ - "count" => 3, - "page_size" => 50, - "users" => users - } - end - - test "only unapproved users", %{conn: conn} do - user = - insert(:user, - nickname: "sadboy", - approval_pending: true, - registration_reason: "Plz let me in!" - ) - - insert(:user, nickname: "happyboy", approval_pending: false) - - conn = get(conn, "/api/pleroma/admin/users?filters=need_approval") - - users = - [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => true, - "url" => user.ap_id, - "registration_reason" => "Plz let me in!", - "actor_type" => "Person" - } - ] - |> Enum.sort_by(& &1["nickname"]) - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => users - } - end - - test "load only admins", %{conn: conn, admin: admin} do - second_admin = insert(:user, is_admin: true) - insert(:user) - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?filters=is_admin") - - users = - [ - %{ - "deactivated" => false, - "id" => admin.id, - "nickname" => admin.nickname, - "roles" => %{"admin" => true, "moderator" => false}, - "local" => admin.local, - "tags" => [], - "avatar" => User.avatar_url(admin) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(admin.name || admin.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => admin.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - }, - %{ - "deactivated" => false, - "id" => second_admin.id, - "nickname" => second_admin.nickname, - "roles" => %{"admin" => true, "moderator" => false}, - "local" => second_admin.local, - "tags" => [], - "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => second_admin.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - |> Enum.sort_by(& &1["nickname"]) - - assert json_response(conn, 200) == %{ - "count" => 2, - "page_size" => 50, - "users" => users - } - end - - test "load only moderators", %{conn: conn} do - moderator = insert(:user, is_moderator: true) - insert(:user) - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => false, - "id" => moderator.id, - "nickname" => moderator.nickname, - "roles" => %{"admin" => false, "moderator" => true}, - "local" => moderator.local, - "tags" => [], - "avatar" => User.avatar_url(moderator) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(moderator.name || moderator.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => moderator.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "load users with tags list", %{conn: conn} do - user1 = insert(:user, tags: ["first"]) - user2 = insert(:user, tags: ["second"]) - insert(:user) - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second") - - users = - [ - %{ - "deactivated" => false, - "id" => user1.id, - "nickname" => user1.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => user1.local, - "tags" => ["first"], - "avatar" => User.avatar_url(user1) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user1.name || user1.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user1.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - }, - %{ - "deactivated" => false, - "id" => user2.id, - "nickname" => user2.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => user2.local, - "tags" => ["second"], - "avatar" => User.avatar_url(user2) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user2.name || user2.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user2.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - |> Enum.sort_by(& &1["nickname"]) - - assert json_response(conn, 200) == %{ - "count" => 2, - "page_size" => 50, - "users" => users - } - end - - test "`active` filters out users pending approval", %{token: token} do - insert(:user, approval_pending: true) - %{id: user_id} = insert(:user, approval_pending: false) - %{id: admin_id} = token.user - - conn = - build_conn() - |> assign(:user, token.user) - |> assign(:token, token) - |> get("/api/pleroma/admin/users?filters=active") - - assert %{ - "count" => 2, - "page_size" => 50, - "users" => [ - %{"id" => ^admin_id}, - %{"id" => ^user_id} - ] - } = json_response(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) - - insert(:user, nickname: "ken", local: true, deactivated: true) - insert(:user, nickname: "bobb", local: false, deactivated: false) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - |> get("/api/pleroma/admin/users?filters=deactivated,external") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "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, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "it omits relay user", %{admin: admin, conn: conn} do - assert %User{} = Relay.get_actor() - - conn = get(conn, "/api/pleroma/admin/users") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => admin.deactivated, - "id" => admin.id, - "nickname" => admin.nickname, - "roles" => %{"admin" => true, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(admin) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(admin.name || admin.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => admin.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - 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) - - conn = - patch( - conn, - "/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] - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}" - 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) - - conn = - patch( - conn, - "/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] - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}" - 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) - - conn = - patch( - conn, - "/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] - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}" - end - - 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") - - assert json_response(conn, 200) == - %{ - "deactivated" => !user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} deactivated users: @#{user.nickname}" - end - - describe "PUT disable_mfa" do - test "returns 200 and disable 2fa", %{conn: conn} do - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true} - } - ) - - response = - conn - |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname}) - |> json_response(200) - - assert response == user.nickname - mfa_settings = refresh_record(user).multi_factor_authentication_settings - - refute mfa_settings.enabled - refute mfa_settings.totp.confirmed - end - - test "returns 404 if user not found", %{conn: conn} do - response = - conn - |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"}) - |> json_response(404) - - assert response == %{"error" => "Not found"} - end - end - - describe "GET /api/pleroma/admin/restart" do - setup do: clear_config(:configurable_from_database, true) - - test "pleroma restarts", %{conn: conn} do - capture_log(fn -> - assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{} - end) =~ "pleroma restarted" - - refute Restarter.Pleroma.need_reboot?() - end - end - - test "need_reboot flag", %{conn: conn} do - assert conn - |> get("/api/pleroma/admin/need_reboot") - |> json_response(200) == %{"need_reboot" => false} - - Restarter.Pleroma.need_reboot() - - assert conn - |> get("/api/pleroma/admin/need_reboot") - |> json_response(200) == %{"need_reboot" => true} - - on_exit(fn -> Restarter.Pleroma.refresh() end) - end - - describe "GET /api/pleroma/admin/users/:nickname/statuses" 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) - - %{user: user} - end - - 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 - 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") - - assert json_response(conn, 200) |> length() == 2 - end - - test "doesn't return private statuses by default", %{conn: conn, user: user} do - {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"}) - - {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) - - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses") - - assert json_response(conn, 200) |> length() == 4 - end - - test "returns private statuses with godmode on", %{conn: conn, user: user} do - {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"}) - - {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) - - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true") - - assert json_response(conn, 200) |> length() == 5 - end - - test "excludes reblogs by default", %{conn: conn, user: user} do - other_user = insert(:user) - {: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 - - conn_res = - get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true") - - assert json_response(conn_res, 200) |> length() == 1 - end - end - - describe "GET /api/pleroma/admin/users/:nickname/chats" do - setup do - user = insert(:user) - recipients = insert_list(3, :user) - - Enum.each(recipients, fn recipient -> - CommonAPI.post_chat_message(user, recipient, "yo") - end) - - %{user: user} - end - - test "renders user's chats", %{conn: conn, user: user} do - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats") - - assert json_response(conn, 200) |> length() == 3 - end - end - - describe "GET /api/pleroma/admin/users/:nickname/chats unauthorized" do - setup do - user = insert(:user) - recipient = insert(:user) - CommonAPI.post_chat_message(user, recipient, "yo") - %{conn: conn} = oauth_access(["read:chats"]) - %{conn: conn, user: user} - end - - test "returns 403", %{conn: conn, user: user} do - conn - |> get("/api/pleroma/admin/users/#{user.nickname}/chats") - |> json_response(403) - end - end - - describe "GET /api/pleroma/admin/users/:nickname/chats unauthenticated" do - setup do - user = insert(:user) - recipient = insert(:user) - CommonAPI.post_chat_message(user, recipient, "yo") - %{conn: build_conn(), user: user} - end - - test "returns 403", %{conn: conn, user: user} do - conn - |> get("/api/pleroma/admin/users/#{user.nickname}/chats") - |> json_response(403) - end - end - - describe "GET /api/pleroma/admin/moderation_log" do - setup do - moderator = insert(:user, is_moderator: true) - - %{moderator: moderator} - end - - test "returns the log", %{conn: conn, admin: admin} do - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_follow", - target: "https://example.org/relay" - }, - inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second) - }) - - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_unfollow", - target: "https://example.org/relay" - }, - inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second) - }) - - conn = get(conn, "/api/pleroma/admin/moderation_log") - - response = json_response(conn, 200) - [first_entry, second_entry] = response["items"] - - assert response["total"] == 2 - assert first_entry["data"]["action"] == "relay_unfollow" - - assert first_entry["message"] == - "@#{admin.nickname} unfollowed relay: https://example.org/relay" - - assert second_entry["data"]["action"] == "relay_follow" - - assert second_entry["message"] == - "@#{admin.nickname} followed relay: https://example.org/relay" - end - - test "returns the log with pagination", %{conn: conn, admin: admin} do - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_follow", - target: "https://example.org/relay" - }, - inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second) - }) - - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_unfollow", - target: "https://example.org/relay" - }, - inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second) - }) - - conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1") - - response1 = json_response(conn1, 200) - [first_entry] = response1["items"] - - assert response1["total"] == 2 - assert response1["items"] |> length() == 1 - assert first_entry["data"]["action"] == "relay_unfollow" - - assert first_entry["message"] == - "@#{admin.nickname} unfollowed relay: https://example.org/relay" - - conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2") - - response2 = json_response(conn2, 200) - [second_entry] = response2["items"] - - assert response2["total"] == 2 - assert response2["items"] |> length() == 1 - assert second_entry["data"]["action"] == "relay_follow" - - assert second_entry["message"] == - "@#{admin.nickname} followed relay: https://example.org/relay" - end - - test "filters log by date", %{conn: conn, admin: admin} do - first_date = "2017-08-15T15:47:06Z" - second_date = "2017-08-20T15:47:06Z" - - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_follow", - target: "https://example.org/relay" - }, - inserted_at: NaiveDateTime.from_iso8601!(first_date) - }) - - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_unfollow", - target: "https://example.org/relay" - }, - inserted_at: NaiveDateTime.from_iso8601!(second_date) - }) - - conn1 = - get( - conn, - "/api/pleroma/admin/moderation_log?start_date=#{second_date}" - ) - - response1 = json_response(conn1, 200) - [first_entry] = response1["items"] - - assert response1["total"] == 1 - assert first_entry["data"]["action"] == "relay_unfollow" - - assert first_entry["message"] == - "@#{admin.nickname} unfollowed relay: https://example.org/relay" - end - - test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_follow", - target: "https://example.org/relay" - } - }) - - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => moderator.id, - "nickname" => moderator.nickname, - "type" => "user" - }, - action: "relay_unfollow", - target: "https://example.org/relay" - } - }) - - conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}") - - response1 = json_response(conn1, 200) - [first_entry] = response1["items"] - - assert response1["total"] == 1 - assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id - end - - test "returns log filtered by search", %{conn: conn, moderator: moderator} do - ModerationLog.insert_log(%{ - actor: moderator, - action: "relay_follow", - target: "https://example.org/relay" - }) - - ModerationLog.insert_log(%{ - actor: moderator, - action: "relay_unfollow", - target: "https://example.org/relay" - }) - - conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo") - - response1 = json_response(conn1, 200) - [first_entry] = response1["items"] - - assert response1["total"] == 1 - - assert get_in(first_entry, ["data", "message"]) == - "@#{moderator.nickname} unfollowed relay: https://example.org/relay" - end - end - - test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated", - %{conn: conn} do - clear_config(Pleroma.Config.get([:instance, :limit_to_local_content]), :unauthenticated) - user = insert(:user, %{local: false, nickname: "u@peer1.com"}) - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials") - - assert json_response(conn, 200) - end - - describe "GET /users/:nickname/credentials" do - test "gets the user credentials", %{conn: conn} do - user = insert(:user) - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials") - - response = assert json_response(conn, 200) - assert response["email"] == user.email - end - - test "returns 403 if requested by a non-admin" do - user = insert(:user) - - conn = - build_conn() - |> assign(:user, user) - |> get("/api/pleroma/admin/users/#{user.nickname}/credentials") - - assert json_response(conn, :forbidden) - end - end - - describe "PATCH /users/:nickname/credentials" do - setup do - user = insert(:user) - [user: user] - end - - test "changes password and email", %{conn: conn, admin: admin, user: user} do - assert user.password_reset_pending == false - - conn = - patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ - "password" => "new_password", - "email" => "new_email@example.com", - "name" => "new_name" - }) - - assert json_response(conn, 200) == %{"status" => "success"} - - ObanHelpers.perform_all() - - updated_user = User.get_by_id(user.id) - - assert updated_user.email == "new_email@example.com" - assert updated_user.name == "new_name" - assert updated_user.password_hash != user.password_hash - assert updated_user.password_reset_pending == true - - [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort() - - assert ModerationLog.get_log_entry_message(log_entry1) == - "@#{admin.nickname} updated users: @#{user.nickname}" - - assert ModerationLog.get_log_entry_message(log_entry2) == - "@#{admin.nickname} forced password reset for users: @#{user.nickname}" - end - - test "returns 403 if requested by a non-admin", %{user: user} do - conn = - build_conn() - |> assign(:user, user) - |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{ - "password" => "new_password", - "email" => "new_email@example.com", - "name" => "new_name" - }) - - assert json_response(conn, :forbidden) - end - - test "changes actor type from permitted list", %{conn: conn, user: user} do - assert user.actor_type == "Person" - - assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ - "actor_type" => "Service" - }) - |> json_response(200) == %{"status" => "success"} - - updated_user = User.get_by_id(user.id) - - assert updated_user.actor_type == "Service" - - assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ - "actor_type" => "Application" - }) - |> json_response(400) == %{"errors" => %{"actor_type" => "is invalid"}} - end - - test "update non existing user", %{conn: conn} do - assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{ - "password" => "new_password" - }) - |> json_response(404) == %{"error" => "Not found"} - end - end - - describe "PATCH /users/:nickname/force_password_reset" do - test "sets password_reset_pending to true", %{conn: conn} do - user = insert(:user) - assert user.password_reset_pending == false - - conn = - patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]}) - - assert empty_json_response(conn) == "" - - ObanHelpers.perform_all() - - assert User.get_by_id(user.id).password_reset_pending == true - end - end - - describe "instances" do - test "GET /instances/:instance/statuses", %{conn: conn} do - user = insert(:user, local: false, nickname: "archaeme@archae.me") - user2 = insert(:user, local: false, nickname: "test@test.com") - insert_pair(:note_activity, user: user) - activity = insert(:note_activity, user: user2) - - ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses") - - response = json_response(ret_conn, 200) - - assert length(response) == 2 - - 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) - - 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 - - ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true") - response = json_response(ret_conn, 200) - assert length(response) == 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) - - assert first_user.confirmation_pending == true - assert second_user.confirmation_pending == true - - ret_conn = - patch(conn, "/api/pleroma/admin/users/confirm_email", %{ - nicknames: [ - first_user.nickname, - second_user.nickname - ] - }) - - assert ret_conn.status == 200 - - assert first_user.confirmation_pending == true - assert second_user.confirmation_pending == true - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{ - second_user.nickname - }" - end - end - - 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) - - ret_conn = - patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{ - nicknames: [ - first_user.nickname, - second_user.nickname - ] - }) - - assert ret_conn.status == 200 - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{ - second_user.nickname - }" - - ObanHelpers.perform_all() - - Pleroma.Emails.UserEmail.account_confirmation_email(first_user) - # temporary hackney fix until hackney max_connections bug is fixed - # https://git.pleroma.social/pleroma/pleroma/-/issues/2101 - |> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) - |> assert_email_sent() - end - end - - describe "/api/pleroma/admin/stats" do - test "status visibility count", %{conn: conn} do - admin = insert(:user, is_admin: true) - user = insert(:user) - CommonAPI.post(user, %{visibility: "public", status: "hey"}) - CommonAPI.post(user, %{visibility: "unlisted", status: "hey"}) - CommonAPI.post(user, %{visibility: "unlisted", status: "hey"}) - - response = - conn - |> assign(:user, admin) - |> get("/api/pleroma/admin/stats") - |> json_response(200) - - assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} = - response["status_visibility"] - end - - test "by instance", %{conn: conn} do - admin = insert(:user, is_admin: true) - user1 = insert(:user) - instance2 = "instance2.tld" - user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"}) - - CommonAPI.post(user1, %{visibility: "public", status: "hey"}) - CommonAPI.post(user2, %{visibility: "unlisted", status: "hey"}) - CommonAPI.post(user2, %{visibility: "private", status: "hey"}) - - response = - conn - |> assign(:user, admin) - |> get("/api/pleroma/admin/stats", instance: instance2) - |> json_response(200) - - assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} = - response["status_visibility"] - end - end -end - -# Needed for testing -defmodule Pleroma.Web.Endpoint.NotReal do -end - -defmodule Pleroma.Captcha.NotReal do -end