Merge remote-tracking branch 'origin/develop' into mutes-blocks-pagination

This commit is contained in:
Egor Kislitsyn 2020-10-29 15:35:42 +04:00
commit fa902867c0
No known key found for this signature in database
GPG key ID: 1B49CB15B71E7805
66 changed files with 861 additions and 421 deletions

View file

@ -198,7 +198,7 @@ amd64:
variables: &release-variables variables: &release-variables
MIX_ENV: prod MIX_ENV: prod
before_script: &before-release 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 - echo "import Mix.Config" > config/prod.secret.exs
- mix local.hex --force - mix local.hex --force
- mix local.rebar --force - mix local.rebar --force
@ -217,7 +217,7 @@ amd64-musl:
cache: *release-cache cache: *release-cache
variables: *release-variables variables: *release-variables
before_script: &before-release-musl 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 - echo "import Mix.Config" > config/prod.secret.exs
- mix local.hex --force - mix local.hex --force
- mix local.rebar --force - mix local.rebar --force

View file

@ -13,12 +13,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Pleroma API: Importing the mutes users from CSV files. - Pleroma API: Importing the mutes users from CSV files.
- Experimental websocket-based federation between Pleroma instances. - Experimental websocket-based federation between Pleroma instances.
- Support pagination of blocks and mutes - Support pagination of blocks and mutes
- App metrics: ability to restrict access to specified IP whitelist.
- Configuration: Add `:instance, autofollowing_nicknames` setting to provide a way to make accounts automatically follow new users that register on the local Pleroma instance.
### Changed ### Changed
- **Breaking** Requires `libmagic` (or `file`) to guess file types. - **Breaking** Requires `libmagic` (or `file`) to guess file types.
- **Breaking:** Pleroma Admin API: emoji packs and files routes changed. - **Breaking:** Pleroma Admin API: emoji packs and files routes changed.
- **Breaking:** Sensitive/NSFW statuses no longer disable link previews. - **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. - Search: Users are now findable by their urls.
- Renamed `:await_up_timeout` in `:connections_pool` namespace to `:connect_timeout`, old name is deprecated. - 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. - Renamed `:timeout` in `pools` namespace to `:recv_timeout`, old name is deprecated.
@ -48,6 +51,9 @@ switched to a new configuration mechanism, however it was not officially removed
- Add documented-but-missing chat pagination. - Add documented-but-missing chat pagination.
- Allow sending out emails again. - 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.
## Unreleased (Patch) ## Unreleased (Patch)

View file

@ -123,7 +123,6 @@
# Configures the endpoint # Configures the endpoint
config :pleroma, Pleroma.Web.Endpoint, config :pleroma, Pleroma.Web.Endpoint,
instrumenters: [Pleroma.Web.Endpoint.Instrumenter],
url: [host: "localhost"], url: [host: "localhost"],
http: [ http: [
ip: {127, 0, 0, 1}, ip: {127, 0, 0, 1},
@ -143,7 +142,7 @@
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl", secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
signing_salt: "CqaoopA2", signing_salt: "CqaoopA2",
render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)], 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, secure_cookie_flag: true,
extra_cookie_attrs: [ extra_cookie_attrs: [
"SameSite=Lax" "SameSite=Lax"
@ -235,6 +234,7 @@
"text/bbcode" "text/bbcode"
], ],
autofollowed_nicknames: [], autofollowed_nicknames: [],
autofollowing_nicknames: [],
max_pinned_statuses: 1, max_pinned_statuses: 1,
attachment_links: false, attachment_links: false,
max_report_comment_size: 1000, max_report_comment_size: 1000,
@ -636,7 +636,12 @@
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false 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, config :pleroma, Pleroma.ScheduledActivity,
daily_user_limit: 25, daily_user_limit: 25,

View file

@ -829,13 +829,13 @@
key: :autofollowed_nicknames, key: :autofollowed_nicknames,
type: {:list, :string}, type: {:list, :string},
description: description:
"Set to nicknames of (local) users that every new user should automatically follow", "Set to nicknames of (local) users that every new user should automatically follow"
suggestions: [ },
"lain", %{
"kaniini", key: :autofollowing_nicknames,
"lanodan", type: {:list, :string},
"rinpatch" description:
] "Set to nicknames of (local) users that automatically follows every newly registered user"
}, },
%{ %{
key: :attachment_links, key: :attachment_links,
@ -3722,5 +3722,42 @@
suggestions: [2] suggestions: [2]
} }
] ]
},
%{
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]
}
]
} }
] ]

View file

@ -2,15 +2,37 @@
Pleroma includes support for exporting metrics via the [prometheus_ex](https://github.com/deadtrickster/prometheus.ex) library. 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` ## `/api/pleroma/app_metrics`
### Exports Prometheus application metrics ### Exports Prometheus application metrics
* Method: `GET` * Method: `GET`
* Authentication: not required * Authentication: not required by default (see configuration options above)
* Params: none * Params: none
* Response: JSON * Response: text
## Grafana ## Grafana
### Config example ### Config example
The following is a config example to use with [Grafana](https://grafana.com) The following is a config example to use with [Grafana](https://grafana.com)
``` ```

View file

@ -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 <https://docs.joinmastodon.org/methods/apps/>
### uploadMedia
Inspired by <https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload>, 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` `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 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` private, there is no public version of them. They are created with a `Create`
activity. activity.
They are part of the `litepub` namespace as `http://litepub.social/ns#ChatMessage`.
Example: Example:
```json ```json

View file

@ -7,97 +7,105 @@ Feel free to contact us to be added to this list!
- Homepage: <https://www.pleroma.com/#desktopApp> - Homepage: <https://www.pleroma.com/#desktopApp>
- Source Code: <https://github.com/roma-apps/roma-desktop> - Source Code: <https://github.com/roma-apps/roma-desktop>
- Platforms: Windows, Mac, Linux - Platforms: Windows, Mac, Linux
- Features: Streaming Ready - Features: MastoAPI, Streaming Ready
### Social ### Social
- Source Code: <https://gitlab.gnome.org/World/Social> - Source Code: <https://gitlab.gnome.org/World/Social>
- Contact: [@brainblasted@social.libre.fi](https://social.libre.fi/users/brainblasted) - Contact: [@brainblasted@social.libre.fi](https://social.libre.fi/users/brainblasted)
- Platforms: Linux (GNOME) - Platforms: Linux (GNOME)
- Note(2019-01-28): Not at a pre-alpha stage yet - Note(2019-01-28): Not at a pre-alpha stage yet
- Features: MastoAPI
### Whalebird ### Whalebird
- Homepage: <https://whalebird.org/> - Homepage: <https://whalebird.org/>
- Source Code: <https://github.com/h3poteto/whalebird-desktop> - Source Code: <https://github.com/h3poteto/whalebird-desktop>
- Contact: [@h3poteto@pleroma.io](https://pleroma.io/users/h3poteto) - Contact: [@h3poteto@pleroma.io](https://pleroma.io/users/h3poteto)
- Platforms: Windows, Mac, Linux - Platforms: Windows, Mac, Linux
- Features: Streaming Ready - Features: MastoAPI, Streaming Ready
## Handheld ## Handheld
### AndStatus
- Homepage: <http://andstatus.org/>
- Source Code: <https://github.com/andstatus/andstatus/>
- Platforms: Android
- Features: MastoAPI, ActivityPub (Client-to-Server)
### Amaroq ### Amaroq
- Homepage: <https://itunes.apple.com/us/app/amaroq-for-mastodon/id1214116200> - Homepage: <https://itunes.apple.com/us/app/amaroq-for-mastodon/id1214116200>
- Source Code: <https://github.com/ReticentJohn/Amaroq> - Source Code: <https://github.com/ReticentJohn/Amaroq>
- Contact: [@eurasierboy@mastodon.social](https://mastodon.social/users/eurasierboy) - Contact: [@eurasierboy@mastodon.social](https://mastodon.social/users/eurasierboy)
- Platforms: iOS - Platforms: iOS
- Features: No Streaming - Features: MastoAPI, No Streaming
### Fedilab ### Fedilab
- Homepage: <https://fedilab.app/> - Homepage: <https://fedilab.app/>
- Source Code: <https://framagit.org/tom79/fedilab/> - Source Code: <https://framagit.org/tom79/fedilab/>
- Contact: [@fedilab@framapiaf.org](https://framapiaf.org/users/fedilab) - Contact: [@fedilab@framapiaf.org](https://framapiaf.org/users/fedilab)
- Platforms: Android - Platforms: Android
- Features: Streaming Ready, Moderation, Text Formatting - Features: MastoAPI, Streaming Ready, Moderation, Text Formatting
### Kyclos ### Kyclos
- Source Code: <https://git.pleroma.social/pleroma/harbour-kyclos> - Source Code: <https://git.pleroma.social/pleroma/harbour-kyclos>
- Platforms: SailfishOS - Platforms: SailfishOS
- Features: No Streaming - Features: MastoAPI, No Streaming
### Husky ### Husky
- Source code: <https://git.mentality.rip/FWGS/Husky> - Source code: <https://git.mentality.rip/FWGS/Husky>
- Contact: [@Husky@enigmatic.observer](https://enigmatic.observer/users/Husky) - Contact: [@Husky@enigmatic.observer](https://enigmatic.observer/users/Husky)
- Platforms: Android - Platforms: Android
- Features: No Streaming, Emoji Reactions, Text Formatting, FE Stickers - Features: MastoAPI, No Streaming, Emoji Reactions, Text Formatting, FE Stickers
### Fedi ### Fedi
- Homepage: <https://www.fediapp.com/> - Homepage: <https://www.fediapp.com/>
- Source Code: Proprietary, but gratis - Source Code: Proprietary, but gratis
- Platforms: iOS, Android - Platforms: iOS, Android
- Features: Pleroma-specific features like Reactions - Features: MastoAPI, Pleroma-specific features like Reactions
### Tusky ### Tusky
- Homepage: <https://tuskyapp.github.io/> - Homepage: <https://tuskyapp.github.io/>
- Source Code: <https://github.com/tuskyapp/Tusky> - Source Code: <https://github.com/tuskyapp/Tusky>
- Contact: [@ConnyDuck@mastodon.social](https://mastodon.social/users/ConnyDuck) - Contact: [@ConnyDuck@mastodon.social](https://mastodon.social/users/ConnyDuck)
- Platforms: Android - Platforms: Android
- Features: No Streaming - Features: MastoAPI, No Streaming
### Twidere ### Twidere
- Homepage: <https://twidere.mariotaku.org/> - Homepage: <https://twidere.mariotaku.org/>
- Source Code: <https://github.com/TwidereProject/Twidere-Android/> - Source Code: <https://github.com/TwidereProject/Twidere-Android/>
- Contact: <me@mariotaku.org> - Contact: <me@mariotaku.org>
- Platform: Android - Platform: Android
- Features: No Streaming - Features: MastoAPI, No Streaming
### Indigenous ### Indigenous
- Homepage: <https://indigenous.realize.be/> - Homepage: <https://indigenous.realize.be/>
- Source Code: <https://github.com/swentel/indigenous-android/> - Source Code: <https://github.com/swentel/indigenous-android/>
- Contact: [@swentel@realize.be](https://realize.be) - Contact: [@swentel@realize.be](https://realize.be)
- Platforms: Android - Platforms: Android
- Features: No Streaming - Features: MastoAPI, No Streaming
## Alternative Web Interfaces ## Alternative Web Interfaces
### Brutaldon ### Brutaldon
- Homepage: <https://jfm.carcosa.net/projects/software/brutaldon/> - Homepage: <https://jfm.carcosa.net/projects/software/brutaldon/>
- Source Code: <https://git.carcosa.net/jmcbray/brutaldon> - Source Code: <https://git.carcosa.net/jmcbray/brutaldon>
- Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc) - Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc)
- Features: No Streaming - Features: MastoAPI, No Streaming
### Halcyon ### Halcyon
- Source Code: <https://notabug.org/halcyon-suite/halcyon> - Source Code: <https://notabug.org/halcyon-suite/halcyon>
- Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon) - Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon)
- Features: Streaming Ready - Features: MastoAPI, Streaming Ready
### Pinafore ### Pinafore
- Homepage: <https://pinafore.social/> - Homepage: <https://pinafore.social/>
- Source Code: <https://github.com/nolanlawson/pinafore> - Source Code: <https://github.com/nolanlawson/pinafore>
- Contact: [@pinafore@mastodon.technology](https://mastodon.technology/users/pinafore) - Contact: [@pinafore@mastodon.technology](https://mastodon.technology/users/pinafore)
- Note: Pleroma support is a secondary goal - Note: Pleroma support is a secondary goal
- Features: No Streaming - Features: MastoAPI, No Streaming
### Sengi ### Sengi
- Homepage: <https://nicolasconstant.github.io/sengi/> - Homepage: <https://nicolasconstant.github.io/sengi/>
- Source Code: <https://github.com/NicolasConstant/sengi> - Source Code: <https://github.com/NicolasConstant/sengi>
- Contact: [@sengi_app@mastodon.social](https://mastodon.social/users/sengi_app) - Contact: [@sengi_app@mastodon.social](https://mastodon.social/users/sengi_app)
- Features: MastoAPI
### DashFE ### DashFE
- Source Code: <https://notabug.org/daisuke/DashboardFE> - Source Code: <https://notabug.org/daisuke/DashboardFE>
@ -107,3 +115,4 @@ Feel free to contact us to be added to this list!
- Source Code: <https://git.freesoftwareextremist.com/bloat/> - Source Code: <https://git.freesoftwareextremist.com/bloat/>
- Contact: [@r@freesoftwareextremist.com](https://freesoftwareextremist.com/users/r) - Contact: [@r@freesoftwareextremist.com](https://freesoftwareextremist.com/users/r)
- Features: Does not requires JavaScript - Features: Does not requires JavaScript
- Features: MastoAPI

View file

@ -45,6 +45,7 @@ To add configuration to your config file, you can copy it from the base config.
older software for theses nicknames. older software for theses nicknames.
* `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature. * `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. * `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. * `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`). * `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`. * `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`.

View file

@ -43,7 +43,7 @@ Other than things bundled in the OTP release Pleroma depends on:
### Installing optional packages ### Installing optional packages
Per [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md): Per [`docs/installation/optional/media_graphics_packages.md`](optional/media_graphics_packages.md):
* ImageMagick * ImageMagick
* ffmpeg * ffmpeg
* exiftool * exiftool

View file

@ -31,8 +31,6 @@ ProtectHome=true
ProtectSystem=full 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. ; 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 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. ; Drops the sysadmin capability from the daemon.
CapabilityBoundingSet=~CAP_SYS_ADMIN CapabilityBoundingSet=~CAP_SYS_ADMIN

View file

@ -31,7 +31,12 @@ def init(%Plug.Conn{method: "GET"} = conn, {endpoint, handler, transport}) do
case conn do case conn do
%{halted: false} = conn -> %{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, socket} ->
{:ok, conn, {__MODULE__, {socket, opts}}} {:ok, conn, {__MODULE__, {socket, opts}}}

View file

@ -100,7 +100,7 @@ def start(_type, _args) do
] ++ ] ++
task_children(@env) ++ task_children(@env) ++
dont_run_in_test(@env) ++ dont_run_in_test(@env) ++
chat_child(@env, chat_enabled?()) ++ chat_child(chat_enabled?()) ++
[ [
Pleroma.Web.Endpoint, Pleroma.Web.Endpoint,
Pleroma.Gopher.Server Pleroma.Gopher.Server
@ -151,7 +151,10 @@ defp setup_instrumenters do
Pleroma.Web.Endpoint.MetricsExporter.setup() Pleroma.Web.Endpoint.MetricsExporter.setup()
Pleroma.Web.Endpoint.PipelineInstrumenter.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 end
defp cachex_children do defp cachex_children do
@ -202,11 +205,14 @@ defp dont_run_in_test(_) do
] ]
end end
defp chat_child(_env, true) do defp chat_child(true) do
[Pleroma.Web.ChatChannel.ChatChannelState] [
Pleroma.Web.ChatChannel.ChatChannelState,
{Phoenix.PubSub, [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]}
]
end end
defp chat_child(_, _), do: [] defp chat_child(_), do: []
defp task_children(:test) do defp task_children(:test) do
[ [

View file

@ -0,0 +1,19 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# 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

View file

@ -426,7 +426,6 @@ def remote_user_changeset(struct \\ %User{local: false}, params) do
params, params,
[ [
:bio, :bio,
:name,
:emoji, :emoji,
:ap_id, :ap_id,
:inbox, :inbox,
@ -455,7 +454,9 @@ def remote_user_changeset(struct \\ %User{local: false}, params) do
:accepts_chat_messages :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) |> unique_constraint(:nickname)
|> validate_format(:nickname, @email_regex) |> validate_format(:nickname, @email_regex)
|> validate_length(:bio, max: bio_limit) |> validate_length(:bio, max: bio_limit)
@ -765,6 +766,16 @@ defp autofollow_users(user) do
follow_all(user, autofollowed_users) follow_all(user, autofollowed_users)
end 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.)" @doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
def register(%Ecto.Changeset{} = changeset) do def register(%Ecto.Changeset{} = changeset) do
with {:ok, user} <- Repo.insert(changeset) do with {:ok, user} <- Repo.insert(changeset) do
@ -774,6 +785,7 @@ def register(%Ecto.Changeset{} = changeset) do
def post_register_action(%User{} = user) do def post_register_action(%User{} = user) do
with {:ok, user} <- autofollow_users(user), with {:ok, user} <- autofollow_users(user),
{:ok, _} <- autofollowing_users(user),
{:ok, user} <- set_cache(user), {:ok, user} <- set_cache(user),
{:ok, _} <- send_welcome_email(user), {:ok, _} <- send_welcome_email(user),
{:ok, _} <- send_welcome_message(user), {:ok, _} <- send_welcome_message(user),

View file

@ -172,7 +172,7 @@ def router do
def channel do def channel do
quote do quote do
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse # credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
use Phoenix.Channel import Phoenix.Channel
import Pleroma.Web.Gettext import Pleroma.Web.Gettext
end end
end end

View file

@ -1378,6 +1378,7 @@ def fetch_and_prepare_user_from_ap_id(ap_id, opts \\ []) do
{:ok, data} <- user_data_from_user_object(data) do {:ok, data} <- user_data_from_user_object(data) do
{:ok, maybe_update_follow_information(data)} {:ok, maybe_update_follow_information(data)}
else else
# If this has been deleted, only log a debug and not an error
{:error, "Object has been deleted" = e} -> {:error, "Object has been deleted" = e} ->
Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}") Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
{:error, e} {:error, e}

View file

@ -414,7 +414,7 @@ defp handle_user_activity(
object = object =
object object
|> Map.merge(Map.take(params, ["to", "cc"])) |> Map.merge(Map.take(params, ["to", "cc"]))
|> Map.put("attributedTo", user.ap_id()) |> Map.put("attributedTo", user.ap_id)
|> Transmogrifier.fix_object() |> Transmogrifier.fix_object()
ActivityPub.create(%{ ActivityPub.create(%{
@ -458,7 +458,7 @@ def update_outbox(
%{assigns: %{user: %User{nickname: nickname} = user}} = conn, %{assigns: %{user: %User{nickname: nickname} = user}} = conn,
%{"nickname" => nickname} = params %{"nickname" => nickname} = params
) do ) do
actor = user.ap_id() actor = user.ap_id
params = params =
params params
@ -525,19 +525,6 @@ defp ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
{new_user, for_user} {new_user, for_user}
end end
@doc """
Endpoint based on <https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload>
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 def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
with {:ok, object} <- with {:ok, object} <-
ActivityPub.upload( ActivityPub.upload(

View file

@ -242,9 +242,7 @@ def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity)
end) end)
end end
@doc """ # Publishes an activity to all relevant peers.
Publishes an activity to all relevant peers.
"""
def publish(%User{} = actor, %Activity{} = activity) do def publish(%User{} = actor, %Activity{} = activity) do
public = is_public?(activity) public = is_public?(activity)

View file

@ -306,6 +306,7 @@ def handle_object_creation(%{"type" => "ChatMessage"} = object, meta) do
streamables = streamables =
[[actor, recipient], [recipient, actor]] [[actor, recipient], [recipient, actor]]
|> Enum.uniq()
|> Enum.map(fn [user, other_user] -> |> Enum.map(fn [user, other_user] ->
if user.local do if user.local do
{:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)

View file

@ -40,6 +40,7 @@ def fix_object(object, options \\ []) do
|> fix_in_reply_to(options) |> fix_in_reply_to(options)
|> fix_emoji |> fix_emoji
|> fix_tag |> fix_tag
|> set_sensitive
|> fix_content_map |> fix_content_map
|> fix_addressing |> fix_addressing
|> fix_summary |> fix_summary
@ -313,19 +314,21 @@ def fix_tag(%{"tag" => tag} = object) when is_list(tag) do
tags = tags =
tag tag
|> Enum.filter(fn data -> data["type"] == "Hashtag" and data["name"] end) |> 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) Map.put(object, "tag", tag ++ tags)
end end
def fix_tag(%{"tag" => %{"type" => "Hashtag", "name" => hashtag} = tag} = object) do def fix_tag(%{"tag" => %{} = tag} = object) do
combined = [tag, String.slice(hashtag, 1..-1)] object
|> Map.put("tag", [tag])
Map.put(object, "tag", combined) |> fix_tag
end end
def fix_tag(%{"tag" => %{} = tag} = object), do: Map.put(object, "tag", [tag])
def fix_tag(object), do: object def fix_tag(object), do: object
# content map usually only has one language so this will do for now. # content map usually only has one language so this will do for now.
@ -927,7 +930,7 @@ def set_conversation(object) do
Map.put(object, "conversation", object["context"]) Map.put(object, "conversation", object["context"])
end end
def set_sensitive(%{"sensitive" => true} = object) do def set_sensitive(%{"sensitive" => _} = object) do
object object
end end

View file

@ -44,29 +44,30 @@ def is_direct?(activity) do
def is_list?(%{data: %{"listMessage" => _}}), do: true def is_list?(%{data: %{"listMessage" => _}}), do: true
def is_list?(_), do: false def is_list?(_), do: false
@spec visible_for_user?(Activity.t(), User.t() | nil) :: boolean() @spec visible_for_user?(Activity.t() | nil, User.t() | nil) :: boolean()
def visible_for_user?(%{actor: ap_id}, %User{ap_id: ap_id}), do: true 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?(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"] || user.ap_id in activity.data["to"] ||
list_ap_id list_ap_id
|> Pleroma.List.get_by_ap_id() |> Pleroma.List.get_by_ap_id()
|> Pleroma.List.member?(user) |> Pleroma.List.member?(user)
end end
def visible_for_user?(%{local: local} = activity, nil) do def visible_for_user?(%Activity{} = activity, nil) do
cfg_key = if local, do: :local, else: :remote if restrict_unauthenticated_access?(activity),
if Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key),
do: false, do: false,
else: is_public?(activity) else: is_public?(activity)
end end
def visible_for_user?(activity, user) do def visible_for_user?(%Activity{} = activity, user) do
x = [user.ap_id | User.following(user)] x = [user.ap_id | User.following(user)]
y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || []) y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || [])
is_public?(activity) || Enum.any?(x, &(&1 in y)) 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 result
end 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 def get_visibility(object) do
to = object.data["to"] || [] to = object.data["to"] || []
cc = object.data["cc"] || [] cc = object.data["cc"] || []

View file

@ -52,7 +52,7 @@ def render("credentials.json", %{user: user, for: for_user}) do
:skip_thread_containment, :skip_thread_containment,
:pleroma_settings_store, :pleroma_settings_store,
:raw_fields, :raw_fields,
:discoverable, :is_discoverable,
:actor_type :actor_type
]) ])
|> Map.merge(%{ |> Map.merge(%{

View file

@ -52,7 +52,7 @@ def render("show.json", %{report: report, user: user, account: account, statuses
end end
def render("index_notes.json", %{notes: notes}) when is_list(notes) do 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 end
def render("index_notes.json", _), do: [] def render("index_notes.json", _), do: []

View file

@ -274,7 +274,7 @@ defp build_attachment_link(_), do: ""
def format_input(text, format, options \\ []) def format_input(text, format, options \\ [])
@doc """ @doc """
Formatting text to plain text. Formatting text to plain text, BBCode, HTML, or Markdown
""" """
def format_input(text, "text/plain", options) do def format_input(text, "text/plain", options) do
text text
@ -285,9 +285,6 @@ def format_input(text, "text/plain", options) do
end).() end).()
end end
@doc """
Formatting text as BBCode.
"""
def format_input(text, "text/bbcode", options) do def format_input(text, "text/bbcode", options) do
text text
|> String.replace(~r/\r/, "") |> String.replace(~r/\r/, "")
@ -297,18 +294,12 @@ def format_input(text, "text/bbcode", options) do
|> Formatter.linkify(options) |> Formatter.linkify(options)
end end
@doc """
Formatting text to html.
"""
def format_input(text, "text/html", options) do def format_input(text, "text/html", options) do
text text
|> Formatter.html_escape("text/html") |> Formatter.html_escape("text/html")
|> Formatter.linkify(options) |> Formatter.linkify(options)
end end
@doc """
Formatting text to markdown.
"""
def format_input(text, "text/markdown", options) do def format_input(text, "text/markdown", options) do
text text
|> Formatter.mentions_escape(options) |> Formatter.mentions_escape(options)

View file

@ -7,8 +7,12 @@ defmodule Pleroma.Web.Endpoint do
require Pleroma.Constants require Pleroma.Constants
alias Pleroma.Config
socket("/socket", Pleroma.Web.UserSocket) socket("/socket", Pleroma.Web.UserSocket)
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
plug(Pleroma.Web.Plugs.SetLocalePlug) plug(Pleroma.Web.Plugs.SetLocalePlug)
plug(CORSPlug) plug(CORSPlug)
plug(Pleroma.Web.Plugs.HTTPSecurityPlug) plug(Pleroma.Web.Plugs.HTTPSecurityPlug)
@ -86,19 +90,19 @@ defmodule Pleroma.Web.Endpoint do
plug(Plug.Parsers, plug(Plug.Parsers,
parsers: [ parsers: [
:urlencoded, :urlencoded,
{:multipart, length: {Pleroma.Config, :get, [[:instance, :upload_limit]]}}, {:multipart, length: {Config, :get, [[:instance, :upload_limit]]}},
:json :json
], ],
pass: ["*/*"], pass: ["*/*"],
json_decoder: Jason, json_decoder: Jason,
length: Pleroma.Config.get([:instance, :upload_limit]), length: Config.get([:instance, :upload_limit]),
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []} body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
) )
plug(Plug.MethodOverride) plug(Plug.MethodOverride)
plug(Plug.Head) plug(Plug.Head)
secure_cookies = Pleroma.Config.get([__MODULE__, :secure_cookie_flag]) secure_cookies = Config.get([__MODULE__, :secure_cookie_flag])
cookie_name = cookie_name =
if secure_cookies, if secure_cookies,
@ -106,7 +110,7 @@ defmodule Pleroma.Web.Endpoint do
else: "pleroma_key" else: "pleroma_key"
extra = extra =
Pleroma.Config.get([__MODULE__, :extra_cookie_attrs]) Config.get([__MODULE__, :extra_cookie_attrs])
|> Enum.join(";") |> Enum.join(";")
# The session will be stored in the cookie and signed, # The session will be stored in the cookie and signed,
@ -116,7 +120,7 @@ defmodule Pleroma.Web.Endpoint do
Plug.Session, Plug.Session,
store: :cookie, store: :cookie,
key: cookie_name, key: cookie_name,
signing_salt: Pleroma.Config.get([__MODULE__, :signing_salt], "CqaoopA2"), signing_salt: Config.get([__MODULE__, :signing_salt], "CqaoopA2"),
http_only: true, http_only: true,
secure: secure_cookies, secure: secure_cookies,
extra: extra extra: extra
@ -136,8 +140,34 @@ defmodule MetricsExporter do
use Prometheus.PlugExporter use Prometheus.PlugExporter
end 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(PipelineInstrumenter)
plug(MetricsExporter)
plug(MetricsExporterCaller)
plug(Pleroma.Web.Router) plug(Pleroma.Web.Router)

View file

@ -10,14 +10,14 @@ defmodule Pleroma.Web.Feed.TagController do
alias Pleroma.Web.Feed.FeedView alias Pleroma.Web.Feed.FeedView
def feed(conn, params) do def feed(conn, params) do
unless Pleroma.Config.restrict_unauthenticated_access?(:activities, :local) do if Config.get!([:instance, :public]) do
render_feed(conn, params) render_feed(conn, params)
else else
render_error(conn, :not_found, "Not found") render_error(conn, :not_found, "Not found")
end end
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) {format, tag} = parse_tag(raw_tag)
activities = activities =
@ -36,12 +36,13 @@ def render_feed(conn, %{"tag" => raw_tag} = params) do
end end
@spec parse_tag(binary() | any()) :: {format :: String.t(), tag :: String.t()} @spec parse_tag(binary() | any()) :: {format :: String.t(), tag :: String.t()}
defp parse_tag(raw_tag) when is_binary(raw_tag) do defp parse_tag(raw_tag) do
case Enum.reverse(String.split(raw_tag, ".")) do case is_binary(raw_tag) && Enum.reverse(String.split(raw_tag, ".")) do
[format | tag] when format in ["atom", "rss"] -> {format, Enum.join(tag, ".")} [format | tag] when format in ["rss", "atom"] ->
_ -> {"rss", raw_tag} {format, Enum.join(tag, ".")}
_ ->
{"atom", raw_tag}
end end
end end
defp parse_tag(raw_tag), do: {"rss", raw_tag}
end end

View file

@ -5,6 +5,7 @@
defmodule Pleroma.Web.Feed.UserController do defmodule Pleroma.Web.Feed.UserController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
alias Pleroma.Config
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.ActivityPubController alias Pleroma.Web.ActivityPub.ActivityPubController
@ -22,12 +23,7 @@ def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname
def feed_redirect(%{assigns: %{format: format}} = conn, _params) def feed_redirect(%{assigns: %{format: format}} = conn, _params)
when format in ["json", "activity+json"] do when format in ["json", "activity+json"] do
with %{halted: false} = conn <- ActivityPubController.call(conn, :user)
Pleroma.Web.Plugs.EnsureAuthenticatedPlug.call(conn,
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
) do
ActivityPubController.call(conn, :user)
end
end end
def feed_redirect(conn, %{"nickname" => nickname}) do def feed_redirect(conn, %{"nickname" => nickname}) do
@ -36,25 +32,18 @@ def feed_redirect(conn, %{"nickname" => nickname}) do
end end
end end
def feed(conn, params) do def feed(conn, %{"nickname" => nickname} = 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
format = get_format(conn) format = get_format(conn)
format = format =
if format in ["rss", "atom"] do if format in ["atom", "rss"] do
format format
else else
"atom" "atom"
end 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 = activities =
%{ %{
type: ["Create"], type: ["Create"],
@ -69,7 +58,7 @@ def render_feed(conn, %{"nickname" => nickname} = params) do
|> render("user.#{format}", |> render("user.#{format}",
user: user, user: user,
activities: activities, activities: activities,
feed_config: Pleroma.Config.get([:feed]) feed_config: Config.get([:feed])
) )
end end
end end
@ -81,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, %User{local: false}}), do: errors(conn, {:error, :not_found})
def errors(conn, {:fetch_user, nil}), 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 def errors(conn, _) do
render_error(conn, :internal_server_error, "Something went wrong") render_error(conn, :internal_server_error, "Something went wrong")
end end

View file

@ -24,7 +24,7 @@ def login(%{assigns: %{user: %User{}}} = conn, _params) do
redirect(conn, to: local_mastodon_root_path(conn)) redirect(conn, to: local_mastodon_root_path(conn))
end end
@doc "Local Mastodon FE login init action" # Local Mastodon FE login init action
def login(conn, %{"code" => auth_token}) do def login(conn, %{"code" => auth_token}) do
with {:ok, app} <- get_or_make_app(), with {:ok, app} <- get_or_make_app(),
{:ok, auth} <- Authorization.get_by_token(app, auth_token), {:ok, auth} <- Authorization.get_by_token(app, auth_token),
@ -35,7 +35,7 @@ def login(conn, %{"code" => auth_token}) do
end end
end end
@doc "Local Mastodon FE callback action" # Local Mastodon FE callback action
def login(conn, _) do def login(conn, _) do
with {:ok, app} <- get_or_make_app() do with {:ok, app} <- get_or_make_app() do
path = path =

View file

@ -127,9 +127,8 @@ def index(%{assigns: %{user: user}} = conn, %{ids: ids} = _params) do
@doc """ @doc """
POST /api/v1/statuses 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( def create(
%{ %{
assigns: %{user: user}, assigns: %{user: user},
@ -160,11 +159,7 @@ def create(
end end
end end
@doc """ # Creates a regular status
POST /api/v1/statuses
Creates a regular status
"""
def create(%{assigns: %{user: user}, body_params: %{status: _} = params} = conn, _) do def create(%{assigns: %{user: user}, body_params: %{status: _} = params} = conn, _) do
params = Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id]) params = Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id])

View file

@ -30,7 +30,7 @@ defp do_purge(method, url, headers, options) do
{:ok, %{status: status} = env} when 400 <= status and status < 500 -> {:ok, %{status: status} = env} when 400 <= status and status < 500 ->
{:error, env} {:error, env}
{:error, error} = error -> {:error, _} = error ->
error error
_ -> _ ->

View file

@ -16,10 +16,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
alias Pleroma.Web.Plugs.RateLimiter alias Pleroma.Web.Plugs.RateLimiter
alias Pleroma.Web.Router alias Pleroma.Web.Router
plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug,
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
)
plug( plug(
RateLimiter, RateLimiter,
[name: :ap_routes, params: ["uuid"]] when action in [:object, :activity] [name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
@ -37,14 +33,12 @@ def object(%{assigns: %{format: format}} = conn, _params)
ActivityPubController.call(conn, :object) ActivityPubController.call(conn, :object)
end end
def object(%{assigns: %{format: format}} = conn, _params) do def object(conn, _params) do
with id <- Endpoint.url() <> conn.request_path, with id <- Endpoint.url() <> conn.request_path,
{_, %Activity{} = activity} <- {_, %Activity{} = activity} <-
{:activity, Activity.get_create_by_object_ap_id_with_object(id)}, {:activity, Activity.get_create_by_object_ap_id_with_object(id)},
{_, true} <- {:public?, Visibility.is_public?(activity)} do {_, true} <- {:public?, Visibility.is_public?(activity)} do
case format do redirect(conn, to: "/notice/#{activity.id}")
_ -> redirect(conn, to: "/notice/#{activity.id}")
end
else else
reason when reason in [{:public?, false}, {:activity, nil}] -> reason when reason in [{:public?, false}, {:activity, nil}] ->
{:error, :not_found} {:error, :not_found}
@ -59,13 +53,11 @@ def activity(%{assigns: %{format: format}} = conn, _params)
ActivityPubController.call(conn, :activity) ActivityPubController.call(conn, :activity)
end end
def activity(%{assigns: %{format: format}} = conn, _params) do def activity(conn, _params) do
with id <- Endpoint.url() <> conn.request_path, with id <- Endpoint.url() <> conn.request_path,
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)}, {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
{_, true} <- {:public?, Visibility.is_public?(activity)} do {_, true} <- {:public?, Visibility.is_public?(activity)} do
case format do redirect(conn, to: "/notice/#{activity.id}")
_ -> redirect(conn, to: "/notice/#{activity.id}")
end
else else
reason when reason in [{:public?, false}, {:activity, nil}] -> reason when reason in [{:public?, false}, {:activity, nil}] ->
{:error, :not_found} {:error, :not_found}
@ -119,6 +111,7 @@ def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do
def notice_player(conn, %{"id" => id}) do def notice_player(conn, %{"id" => id}) do
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id), with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
true <- Visibility.is_public?(activity), true <- Visibility.is_public?(activity),
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
%Object{} = object <- Object.normalize(activity), %Object{} = object <- Object.normalize(activity),
%{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object, %{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do

View file

@ -34,22 +34,26 @@ def init(opts) do
end end
def call(conn, opts) do def call(conn, opts) do
frontend_type = Map.get(opts, :frontend_type, :primary) with false <- invalid_path?(conn.path_info),
path = file_path("", frontend_type) frontend_type <- Map.get(opts, :frontend_type, :primary),
path when not is_nil(path) <- file_path("", frontend_type) do
if path do call_static(conn, opts, path)
conn
|> call_static(opts, path)
else else
conn _ ->
conn
end end
end end
defp call_static(conn, opts, from) do defp invalid_path?(list) do
opts = invalid_path?(list, :binary.compile_pattern(["/", "\\", ":", "\0"]))
opts end
|> Map.put(:from, from)
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) Plug.Static.call(conn, opts)
end end
end end

View file

@ -5,6 +5,26 @@
defmodule Pleroma.Web.Router do defmodule Pleroma.Web.Router do
use Pleroma.Web, :router 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 pipeline :browser do
plug(:accepts, ["html"]) plug(:accepts, ["html"])
plug(:fetch_session) plug(:fetch_session)
@ -566,30 +586,43 @@ defmodule Pleroma.Web.Router do
) )
end end
pipeline :ostatus do
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
plug(Pleroma.Web.Plugs.StaticFEPlug)
end
pipeline :oembed do
plug(:accepts, ["json", "xml"])
end
scope "/", Pleroma.Web do 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("/objects/:uuid", OStatus.OStatusController, :object)
get("/activities/:uuid", OStatus.OStatusController, :activity) get("/activities/:uuid", OStatus.OStatusController, :activity)
get("/notice/:id", OStatus.OStatusController, :notice) get("/notice/:id", OStatus.OStatusController, :notice)
get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)
# Mastodon compatibility routes # Mastodon compatibility routes
get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object) get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity) 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", 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) get("/tags/:tag", Feed.TagController, :feed, as: :tag_feed)
end end

View file

@ -17,12 +17,96 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
plug(:put_view, Pleroma.Web.StaticFE.StaticFEView) plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)
plug(:assign_id) plug(:assign_id)
plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug,
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
)
@page_keys ["max_id", "min_id", "limit", "since_id", "order"] @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), defp get_title(%Object{data: %{"name" => name}}) when is_binary(name),
do: name do: name
@ -81,91 +165,6 @@ defp represent(%Activity{object: %Object{data: data}} = activity, selected) do
} }
end 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), defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
do: assign(conn, :notice_id, notice_id) do: assign(conn, :notice_id, notice_id)

View file

@ -228,7 +228,7 @@
<body> <body>
<div class="container"> <div class="container">
<h1><%= Pleroma.Config.get([:instance, :name]) %></h1> <h1><%= Pleroma.Config.get([:instance, :name]) %></h1>
<%= render @view_module, @view_template, assigns %> <%= @inner_content %>
</div> </div>
</body> </body>
</html> </html>

View file

@ -181,7 +181,7 @@
</div> </div>
</div> </div>
<% end %> <% end %>
<%= render @view_module, @view_template, assigns %> <%= @inner_content %>
</td> </td>
</tr> </tr>

View file

@ -10,7 +10,7 @@ video, audio {
} }
</style> </style>
<%= render @view_module, @view_template, assigns %> <%= @inner_content %>
</body> </body>
</html> </html>

View file

@ -9,7 +9,7 @@
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<%= render @view_module, @view_template, assigns %> <%= @inner_content %>
</div> </div>
</body> </body>
</html> </html>

13
mix.exs
View file

@ -114,10 +114,10 @@ defp oauth_deps do
# Type `mix help deps` for examples and options. # Type `mix help deps` for examples and options.
defp deps do defp deps do
[ [
{:phoenix, "~> 1.4.17"}, {:phoenix, "~> 1.5.5"},
{:tzdata, "~> 1.0.3"}, {:tzdata, "~> 1.0.3"},
{:plug_cowboy, "~> 2.3"}, {:plug_cowboy, "~> 2.3"},
{:phoenix_pubsub, "~> 1.1"}, {:phoenix_pubsub, "~> 2.0"},
{:phoenix_ecto, "~> 4.0"}, {:phoenix_ecto, "~> 4.0"},
{:ecto_enum, "~> 1.4"}, {:ecto_enum, "~> 1.4"},
{:ecto_sql, "~> 3.4.4"}, {:ecto_sql, "~> 3.4.4"},
@ -165,9 +165,16 @@ defp deps do
{:telemetry, "~> 0.3"}, {:telemetry, "~> 0.3"},
{:poolboy, "~> 1.5"}, {:poolboy, "~> 1.5"},
{:prometheus, "~> 4.6"}, {: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_plugs, "~> 1.1"},
{:prometheus_phoenix, "~> 1.3"}, {: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"}, {:prometheus_ecto, "~> 1.4"},
{:recon, "~> 2.5"}, {:recon, "~> 2.5"},
{:quack, "~> 0.1.1"}, {:quack, "~> 0.1.1"},

View file

@ -18,8 +18,9 @@
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, "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"}, "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": {: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"}, "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"}, "crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
"crypt": {:git, "https://github.com/msantos/crypt.git", "f63a705f92c26955977ee62a313012e309a4d77a", [ref: "f63a705f92c26955977ee62a313012e309a4d77a"]}, "crypt": {:git, "https://github.com/msantos/crypt.git", "f63a705f92c26955977ee62a313012e309a4d77a", [ref: "f63a705f92c26955977ee62a313012e309a4d77a"]},
"custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"}, "custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"},
@ -85,14 +86,14 @@
"p1_utils": {:hex, :p1_utils, "1.0.18", "3fe224de5b2e190d730a3c5da9d6e8540c96484cf4b4692921d1e28f0c32b01c", [:rebar3], [], "hexpm", "1fc8773a71a15553b179c986b22fbeead19b28fe486c332d4929700ffeb71f88"}, "p1_utils": {:hex, :p1_utils, "1.0.18", "3fe224de5b2e190d730a3c5da9d6e8540c96484cf4b4692921d1e28f0c32b01c", [:rebar3], [], "hexpm", "1fc8773a71a15553b179c986b22fbeead19b28fe486c332d4929700ffeb71f88"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, "parse_trans": {: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"}, "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": {: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.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_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_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_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
"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_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": {: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_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.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"}, "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"}, "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"}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"},
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
@ -100,8 +101,9 @@
"pot": {:hex, :pot, "0.11.0", "61bad869a94534739dd4614a25a619bc5c47b9970e9a0ea5bef4628036fc7a16", [:rebar3], [], "hexpm", "57ee6ee6bdeb639661ffafb9acefe3c8f966e45394de6a766813bb9e1be4e54b"}, "pot": {:hex, :pot, "0.11.0", "61bad869a94534739dd4614a25a619bc5c47b9970e9a0ea5bef4628036fc7a16", [:rebar3], [], "hexpm", "57ee6ee6bdeb639661ffafb9acefe3c8f966e45394de6a766813bb9e1be4e54b"},
"prometheus": {:hex, :prometheus, "4.6.0", "20510f381db1ccab818b4cf2fac5fa6ab5cc91bc364a154399901c001465f46f", [:mix, :rebar3], [], "hexpm", "4905fd2992f8038eccd7aa0cd22f40637ed618c0bed1f75c05aacec15b7545de"}, "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_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_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"}, "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"}, "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"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
@ -110,13 +112,13 @@
"sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"}, "sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"}, "ssl_verify_fun": {: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"}, "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"}, "syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"}, "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/", "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"}, "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"}, "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"}, "ueberauth": {:hex, :ueberauth, "0.6.3", "d42ace28b870e8072cf30e32e385579c57b9cc96ec74fa1f30f30da9c14f3cc0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "afc293d8a1140d6591b53e3eaf415ca92842cb1d32fad3c450c6f045f7f91b60"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"},
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"}, "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},

View file

@ -3,8 +3,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-09-20 13:18+0000\n" "POT-Creation-Date: 2020-09-20 13:18+0000\n"
"PO-Revision-Date: 2020-09-20 14:48+0000\n" "PO-Revision-Date: 2020-10-22 18:25+0000\n"
"Last-Translator: Kana <gudzpoz@live.com>\n" "Last-Translator: shironeko <shironeko@tesaguri.club>\n"
"Language-Team: Chinese (Simplified) <https://translate.pleroma.social/" "Language-Team: Chinese (Simplified) <https://translate.pleroma.social/"
"projects/pleroma/pleroma/zh_Hans/>\n" "projects/pleroma/pleroma/zh_Hans/>\n"
"Language: zh_Hans\n" "Language: zh_Hans\n"
@ -49,7 +49,7 @@ msgstr "是被保留的"
## From Ecto.Changeset.validate_confirmation/3 ## From Ecto.Changeset.validate_confirmation/3
msgid "does not match confirmation" msgid "does not match confirmation"
msgstr "" msgstr "与验证不符"
## From Ecto.Changeset.no_assoc_constraint/3 ## From Ecto.Changeset.no_assoc_constraint/3
msgid "is still associated with this entry" msgid "is still associated with this entry"
@ -138,12 +138,12 @@ msgstr "不能获取收藏"
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:438 #: lib/pleroma/web/activity_pub/activity_pub_controller.ex:438
#, elixir-format #, elixir-format
msgid "Can't like object" msgid "Can't like object"
msgstr "" msgstr "不能喜欢对象"
#: lib/pleroma/web/common_api/utils.ex:563 #: lib/pleroma/web/common_api/utils.ex:563
#, elixir-format #, elixir-format
msgid "Cannot post an empty status without attachments" msgid "Cannot post an empty status without attachments"
msgstr "" msgstr "无法发送空白且不包含附件的状态"
#: lib/pleroma/web/common_api/utils.ex:511 #: lib/pleroma/web/common_api/utils.ex:511
#, elixir-format #, elixir-format
@ -153,100 +153,100 @@ msgstr ""
#: lib/pleroma/config/config_db.ex:191 #: lib/pleroma/config/config_db.ex:191
#, elixir-format #, elixir-format
msgid "Config with params %{params} not found" 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:181
#: lib/pleroma/web/common_api/common_api.ex:185 #: lib/pleroma/web/common_api/common_api.ex:185
#, elixir-format #, elixir-format
msgid "Could not delete" msgid "Could not delete"
msgstr "" msgstr "无法删除"
#: lib/pleroma/web/common_api/common_api.ex:231 #: lib/pleroma/web/common_api/common_api.ex:231
#, elixir-format #, elixir-format
msgid "Could not favorite" msgid "Could not favorite"
msgstr "" msgstr "无法收藏"
#: lib/pleroma/web/common_api/common_api.ex:453 #: lib/pleroma/web/common_api/common_api.ex:453
#, elixir-format #, elixir-format
msgid "Could not pin" msgid "Could not pin"
msgstr "" msgstr "无法置顶"
#: lib/pleroma/web/common_api/common_api.ex:278 #: lib/pleroma/web/common_api/common_api.ex:278
#, elixir-format #, elixir-format
msgid "Could not unfavorite" msgid "Could not unfavorite"
msgstr "" msgstr "无法取消收藏"
#: lib/pleroma/web/common_api/common_api.ex:463 #: lib/pleroma/web/common_api/common_api.ex:463
#, elixir-format #, elixir-format
msgid "Could not unpin" msgid "Could not unpin"
msgstr "" msgstr "无法取消置顶"
#: lib/pleroma/web/common_api/common_api.ex:216 #: lib/pleroma/web/common_api/common_api.ex:216
#, elixir-format #, elixir-format
msgid "Could not unrepeat" msgid "Could not unrepeat"
msgstr "" msgstr "无法取消转发"
#: lib/pleroma/web/common_api/common_api.ex:512 #: lib/pleroma/web/common_api/common_api.ex:512
#: lib/pleroma/web/common_api/common_api.ex:521 #: lib/pleroma/web/common_api/common_api.ex:521
#, elixir-format #, elixir-format
msgid "Could not update state" msgid "Could not update state"
msgstr "" msgstr "无法更新状态"
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:207 #: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:207
#, elixir-format #, elixir-format
msgid "Error." msgid "Error."
msgstr "" msgstr "错误。"
#: lib/pleroma/web/twitter_api/twitter_api.ex:106 #: lib/pleroma/web/twitter_api/twitter_api.ex:106
#, elixir-format #, elixir-format
msgid "Invalid CAPTCHA" msgid "Invalid CAPTCHA"
msgstr "" msgstr "无效的验证码"
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:116 #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:116
#: lib/pleroma/web/oauth/oauth_controller.ex:568 #: lib/pleroma/web/oauth/oauth_controller.ex:568
#, elixir-format #, elixir-format
msgid "Invalid credentials" msgid "Invalid credentials"
msgstr "" msgstr "无效的凭据"
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:38 #: lib/pleroma/plugs/ensure_authenticated_plug.ex:38
#, elixir-format #, elixir-format
msgid "Invalid credentials." msgid "Invalid credentials."
msgstr "" msgstr "无效的凭据。"
#: lib/pleroma/web/common_api/common_api.ex:355 #: lib/pleroma/web/common_api/common_api.ex:355
#, elixir-format #, elixir-format
msgid "Invalid indices" msgid "Invalid indices"
msgstr "" msgstr "无效的索引"
#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:29 #: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:29
#, elixir-format #, elixir-format
msgid "Invalid parameters" msgid "Invalid parameters"
msgstr "" msgstr "无效的参数"
#: lib/pleroma/web/common_api/utils.ex:414 #: lib/pleroma/web/common_api/utils.ex:414
#, elixir-format #, elixir-format
msgid "Invalid password." msgid "Invalid password."
msgstr "" msgstr "无效的密码。"
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:220 #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:220
#, elixir-format #, elixir-format
msgid "Invalid request" msgid "Invalid request"
msgstr "" msgstr "无效的请求"
#: lib/pleroma/web/twitter_api/twitter_api.ex:109 #: lib/pleroma/web/twitter_api/twitter_api.ex:109
#, elixir-format #, elixir-format
msgid "Kocaptcha service unavailable" msgid "Kocaptcha service unavailable"
msgstr "" msgstr "Kocaptcha 服务不可用"
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:112 #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:112
#, elixir-format #, elixir-format
msgid "Missing parameters" msgid "Missing parameters"
msgstr "" msgstr "缺少参数"
#: lib/pleroma/web/common_api/utils.ex:547 #: lib/pleroma/web/common_api/utils.ex:547
#, elixir-format #, elixir-format
msgid "No such conversation" 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:388
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:414 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:456 #: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:414 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:456

View file

@ -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": "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span> #moo</p>",
"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"
}

46
test/fixtures/mewmew_no_name.json vendored Normal file
View file

@ -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"
}

View file

@ -400,7 +400,7 @@ test "dismisses the notification on follow request rejection" do
user = insert(:user, is_locked: true) user = insert(:user, is_locked: true)
follower = insert(:user) follower = insert(:user)
{:ok, _, _, _follow_activity} = CommonAPI.follow(follower, 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) {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
assert [] = Notification.for_user(user) assert [] = Notification.for_user(user)
end end

View file

@ -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, :autofollowed_nicknames])
setup do: clear_config([:instance, :autofollowing_nicknames])
setup do: clear_config([:welcome]) setup do: clear_config([:welcome])
setup do: clear_config([:instance, :account_activation_required]) 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) refute User.following?(registered_user, remote_user)
end 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 test "it sends a welcome message if it is set" do
welcome_user = insert(:user) welcome_user = insert(:user)
Pleroma.Config.put([:welcome, :direct_message, :enabled], true) Pleroma.Config.put([:welcome, :direct_message, :enabled], true)

View file

@ -156,21 +156,6 @@ test "it returns error when user is not found", %{conn: conn} do
assert response == "Not found" assert response == "Not found"
end 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 end
describe "mastodon compatibility routes" do 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) assert "Not found" == json_response(conn2, :not_found)
end 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 end
describe "/activities/:uuid" do 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) assert "Not found" == json_response(conn2, :not_found)
end 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 end
describe "/inbox" do 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"] assert response(conn, 200) =~ announce_activity.data["object"]
end 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 end
describe "POST /users/:nickname/outbox (C2S)" do describe "POST /users/:nickname/outbox (C2S)" do

View file

@ -505,22 +505,22 @@ test "increases replies count", %{user: user} do
# public # public
{:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "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 assert object.data["repliesCount"] == 1
# unlisted # unlisted
{:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "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 assert object.data["repliesCount"] == 2
# private # private
{:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "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 assert object.data["repliesCount"] == 2
# direct # direct
{:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "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 assert object.data["repliesCount"] == 2
end end
end end
@ -2273,4 +2273,15 @@ test "`following` still contains self-replies by friends" do
assert length(activities) == 2 assert length(activities) == 2
end end
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 end

View file

@ -21,7 +21,7 @@ test "it's allowed when address is public" do
"type" => "Create" "type" => "Create"
} }
assert {:ok, message} = RejectNonPublic.filter(message) assert {:ok, _message} = RejectNonPublic.filter(message)
end end
test "it's allowed when cc address contain public address" do 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" "type" => "Create"
} }
assert {:ok, message} = RejectNonPublic.filter(message) assert {:ok, _message} = RejectNonPublic.filter(message)
end end
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) Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], true)
assert {:ok, message} = RejectNonPublic.filter(message) assert {:ok, _message} = RejectNonPublic.filter(message)
end end
test "it's rejected when addrer of message in the follower addresses of user and it disabled in config" do 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) Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], true)
assert {:ok, message} = RejectNonPublic.filter(message) assert {:ok, _message} = RejectNonPublic.filter(message)
end end
test "it's reject when direct messages aren't allow" do test "it's reject when direct messages aren't allow" do

View file

@ -29,7 +29,7 @@ test "allows non-local follow requests" do
actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"]) actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"])
follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: true) follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: true)
message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id} 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
end end

View file

@ -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"]) _user = insert(:user, local: false, ap_id: data["actor"])
assert {:error, e} = Transmogrifier.handle_incoming(data) assert {:error, _e} = Transmogrifier.handle_incoming(data)
end end
test "it does not clobber the addressing on announce activities" do test "it does not clobber the addressing on announce activities" do

View file

@ -101,7 +101,7 @@ test "it fetches reply-to activities if we don't have them" do
{:ok, returned_activity} = Transmogrifier.handle_incoming(data) {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
returned_object = Object.normalize(returned_activity, false) returned_object = Object.normalize(returned_activity, false)
assert activity = assert %Activity{} =
Activity.get_create_by_object_ap_id( Activity.get_create_by_object_ap_id(
"https://mstdn.io/users/mayuutann/statuses/99568293732299394" "https://mstdn.io/users/mayuutann/statuses/99568293732299394"
) )
@ -206,6 +206,16 @@ test "it works for incoming notices" do
assert user.note_count == 1 assert user.note_count == 1
end 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 test "it works for incoming notices with hashtags" do
data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!() data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!()

View file

@ -95,6 +95,20 @@ test "it blocks and does not federate if outgoing blocks are disabled", %{
describe "posting chat messages" do describe "posting chat messages" do
setup do: clear_config([:instance, :chat_limit]) 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 test "it posts a chat message without content but with an attachment" do
author = insert(:user) author = insert(:user)
recipient = insert(:user) recipient = insert(:user)
@ -622,7 +636,7 @@ test "it validates character limits are correctly enforced" do
assert {:error, "The status is over the character limit"} = assert {:error, "The status is over the character limit"} =
CommonAPI.post(user, %{status: "foobar"}) CommonAPI.post(user, %{status: "foobar"})
assert {:ok, activity} = CommonAPI.post(user, %{status: "12345"}) assert {:ok, _activity} = CommonAPI.post(user, %{status: "12345"})
end end
test "it can handle activities that expire" do test "it can handle activities that expire" do

View file

@ -0,0 +1,68 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# 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

View file

@ -52,7 +52,7 @@ test "multiple origins can be added" do
end end
test "will be ignored" do 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) FedRegistry.get_fed_socket(@good_domain_origin)
assert origin == "good.domain:80" assert origin == "good.domain:80"
@ -63,7 +63,7 @@ test "will be ignored" do
test "the newer process will be closed" do test "the newer process will be closed" do
pid_two = build_test_socket(@good_domain) 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) FedRegistry.get_fed_socket(@good_domain_origin)
assert origin == "good.domain:80" assert origin == "good.domain:80"

View file

@ -8,6 +8,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
import Pleroma.Factory import Pleroma.Factory
import SweetXml import SweetXml
alias Pleroma.Config
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Feed.FeedView alias Pleroma.Web.Feed.FeedView
@ -15,7 +16,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
setup do: clear_config([:feed]) setup do: clear_config([:feed])
test "gets a feed (ATOM)", %{conn: conn} do test "gets a feed (ATOM)", %{conn: conn} do
Pleroma.Config.put( Config.put(
[:feed, :post_title], [:feed, :post_title],
%{max_length: 25, omission: "..."} %{max_length: 25, omission: "..."}
) )
@ -82,7 +83,7 @@ test "gets a feed (ATOM)", %{conn: conn} do
end end
test "gets a feed (RSS)", %{conn: conn} do test "gets a feed (RSS)", %{conn: conn} do
Pleroma.Config.put( Config.put(
[:feed, :post_title], [:feed, :post_title],
%{max_length: 25, omission: "..."} %{max_length: 25, omission: "..."}
) )
@ -157,7 +158,7 @@ test "gets a feed (RSS)", %{conn: conn} do
response = response =
conn conn
|> put_req_header("accept", "application/rss+xml") |> put_req_header("accept", "application/rss+xml")
|> get(tag_feed_path(conn, :feed, "pleromaart")) |> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|> response(200) |> response(200)
xml = parse(response) xml = parse(response)
@ -183,14 +184,12 @@ test "gets a feed (RSS)", %{conn: conn} do
end end
describe "private instance" do 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 test "returns 404 for tags feed", %{conn: conn} do
Config.put([:instance, :public], false)
conn conn
|> put_req_header("accept", "application/rss+xml") |> put_req_header("accept", "application/rss+xml")
|> get(tag_feed_path(conn, :feed, "pleromaart")) |> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|> response(404) |> response(404)
end end
end end

View file

@ -13,7 +13,7 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
setup do: clear_config([:instance, :federating], true) setup do: clear_config([:static_fe, :enabled], false)
describe "feed" do describe "feed" do
setup do: clear_config([:feed]) 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)) |> get(user_feed_path(conn, :feed, user.nickname))
|> response(404) |> response(404)
end 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 end
# Note: see ActivityPubControllerTest for JSON format tests # Note: see ActivityPubControllerTest for JSON format tests

View file

@ -32,7 +32,7 @@ test "works by id" do
test "works by nickname" do test "works by nickname" do
user = insert(:user) user = insert(:user)
assert %{"id" => user_id} = assert %{"id" => _user_id} =
build_conn() build_conn()
|> get("/api/v1/accounts/#{user.nickname}") |> get("/api/v1/accounts/#{user.nickname}")
|> json_response_and_validate_schema(200) |> 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) user = insert(:user, nickname: "user@example.com", local: false)
assert %{"id" => user_id} = assert %{"id" => _user_id} =
build_conn() build_conn()
|> get("/api/v1/accounts/#{user.nickname}") |> get("/api/v1/accounts/#{user.nickname}")
|> json_response_and_validate_schema(200) |> json_response_and_validate_schema(200)
@ -1429,10 +1429,10 @@ test "returns an error if captcha is invalid", %{conn: conn} do
test "returns lists to which the account belongs" do test "returns lists to which the account belongs" do
%{user: user, conn: conn} = oauth_access(["read:lists"]) %{user: user, conn: conn} = oauth_access(["read:lists"])
other_user = insert(:user) 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) {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
assert [%{"id" => list_id, "title" => "Test List"}] = assert [%{"id" => _list_id, "title" => "Test List"}] =
conn conn
|> get("/api/v1/accounts/#{other_user.id}/lists") |> get("/api/v1/accounts/#{other_user.id}/lists")
|> json_response_and_validate_schema(200) |> json_response_and_validate_schema(200)

View file

@ -937,7 +937,7 @@ test "reblogged status for another user" do
|> get("/api/v1/statuses/#{reblog_activity1.id}") |> get("/api/v1/statuses/#{reblog_activity1.id}")
assert %{ assert %{
"reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2}, "reblog" => %{"id" => _id, "reblogged" => false, "reblogs_count" => 2},
"reblogged" => false, "reblogged" => false,
"favourited" => false, "favourited" => false,
"bookmarked" => false "bookmarked" => false

View file

@ -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 redirect_query = URI.parse(redirected_to(conn)).query
assert %{"state" => state_param} = URI.decode_query(redirect_query) assert %{"state" => state_param} = URI.decode_query(redirect_query)
@ -119,7 +119,7 @@ test "with user-bound registration, GET /oauth/<provider>/callback redirects to
} }
) )
assert response = html_response(conn, 302) assert html_response(conn, 302)
assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/
end end
@ -182,7 +182,7 @@ test "on authentication error, GET /oauth/<provider>/callback redirects to `redi
} }
) )
assert response = html_response(conn, 302) assert html_response(conn, 302)
assert redirected_to(conn) == app.redirect_uris assert redirected_to(conn) == app.redirect_uris
assert get_flash(conn, :error) == "Failed to authenticate: (error description)." assert get_flash(conn, :error) == "Failed to authenticate: (error description)."
end 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=.+/ assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/
end 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 end
test "with invalid params, POST /oauth/register?op=register renders registration_details page", 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=.+/ assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/
end 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 end
test "with invalid params, POST /oauth/register?op=connect renders registration_details page", test "with invalid params, POST /oauth/register?op=connect renders registration_details page",

View file

@ -7,7 +7,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
import Pleroma.Factory import Pleroma.Factory
alias Pleroma.Config
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
@ -21,7 +20,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
:ok :ok
end end
setup do: clear_config([:instance, :federating], true) setup do: clear_config([:static_fe, :enabled], false)
describe "Mastodon compatibility routes" do describe "Mastodon compatibility routes" do
setup %{conn: conn} do setup %{conn: conn} do
@ -215,15 +214,16 @@ test "404s a non-existing notice", %{conn: conn} do
assert response(conn, 404) assert response(conn, 404)
end end
test "it requires authentication if instance is NOT federating", %{ test "does not require authentication on non-federating instances", %{
conn: conn conn: conn
} do } do
user = insert(:user) clear_config([:instance, :federating], false)
note_activity = insert(:note_activity) note_activity = insert(:note_activity)
conn = put_req_header(conn, "accept", "text/html") conn
|> put_req_header("accept", "text/html")
ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}", user) |> get("/notice/#{note_activity.id}")
|> response(200)
end end
end end
@ -325,14 +325,16 @@ test "404s when attachment isn't audio or video", %{conn: conn} do
|> response(404) |> response(404)
end end
test "it requires authentication if instance is NOT federating", %{ test "does not require authentication on non-federating instances", %{
conn: conn, conn: conn,
note_activity: note_activity note_activity: note_activity
} do } do
user = insert(:user) clear_config([:instance, :federating], false)
conn = put_req_header(conn, "accept", "text/html")
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 end
end end

View file

@ -569,7 +569,7 @@ test "shows pack.json", %{conn: conn} do
test "for pack name with special chars", %{conn: conn} do test "for pack name with special chars", %{conn: conn} do
assert %{ assert %{
"files" => files, "files" => _files,
"files_count" => 1, "files_count" => 1,
"pack" => %{ "pack" => %{
"can-download" => true, "can-download" => true,

View file

@ -34,7 +34,7 @@ test "mascot upload" do
|> put_req_header("content-type", "multipart/form-data") |> put_req_header("content-type", "multipart/form-data")
|> put("/api/v1/pleroma/mascot", %{"file" => file}) |> 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 end
test "mascot retrieving" do test "mascot retrieving" do

View file

@ -4,6 +4,7 @@
defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
import Mock
@dir "test/tmp/instance_static" @dir "test/tmp/instance_static"
@ -53,4 +54,24 @@ test "overrides existing static files for the `pleroma/admin` path", %{conn: con
index = get(conn, "/pleroma/admin/") index = get(conn, "/pleroma/admin/")
assert html_response(index, 200) == "from frontend plug" assert html_response(index, 200) == "from frontend plug"
end 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 end

View file

@ -6,14 +6,12 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
import Pleroma.Factory import Pleroma.Factory
setup_all do: clear_config([:static_fe, :enabled], true) setup_all do: clear_config([:static_fe, :enabled], true)
setup do: clear_config([:instance, :federating], true)
setup %{conn: conn} do setup %{conn: conn} do
conn = put_req_header(conn, "accept", "text/html") conn = put_req_header(conn, "accept", "text/html")
@ -74,8 +72,27 @@ test "pagination, page 2", %{conn: conn, user: user} do
refute html =~ ">test29<" refute html =~ ">test29<"
end end
test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do test "does not require authentication on non-federating instances", %{
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}", user) 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
end end
@ -187,10 +204,28 @@ test "302 for remote cached status", %{conn: conn, user: user} do
assert html_response(conn, 302) =~ "redirected" assert html_response(conn, 302) =~ "redirected"
end 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!"}) {: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 end
end end

View file

@ -22,7 +22,7 @@ defmodule Pleroma.Web.ChannelCase do
using do using do
quote do quote do
# Import conveniences for testing with channels # Import conveniences for testing with channels
use Phoenix.ChannelTest import Phoenix.ChannelTest
use Pleroma.Tests.Helpers use Pleroma.Tests.Helpers
# The default endpoint for testing # The default endpoint for testing

View file

@ -22,7 +22,8 @@ defmodule Pleroma.Web.ConnCase do
using do using do
quote do quote do
# Import conveniences for testing with connections # Import conveniences for testing with connections
use Phoenix.ConnTest import Plug.Conn
import Phoenix.ConnTest
use Pleroma.Tests.Helpers use Pleroma.Tests.Helpers
import Pleroma.Web.Router.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 defp json_response_and_validate_schema(conn, _status) do
flunk("Response schema not found for #{conn.method} #{conn.request_path} #{conn.status}") flunk("Response schema not found for #{conn.method} #{conn.request_path} #{conn.status}")
end 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
end end