Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into emr_develop
This commit is contained in:
commit
6d19014af0
|
@ -12,12 +12,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Media preview proxy (requires `ffmpeg` and `ImageMagick` to be installed and media proxy to be enabled; see `:media_preview_proxy` config for more details).
|
||||
- Pleroma API: Importing the mutes users from CSV files.
|
||||
- Experimental websocket-based federation between Pleroma instances.
|
||||
- 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
|
||||
|
||||
- **Breaking** Requires `libmagic` (or `file`) to guess file types.
|
||||
- **Breaking:** Pleroma Admin API: emoji packs and files routes changed.
|
||||
- **Breaking:** Sensitive/NSFW statuses no longer disable link previews.
|
||||
- **Breaking:** App metrics endpoint (`/api/pleroma/app_metrics`) is disabled by default, check `docs/API/prometheus.md` on enabling and configuring.
|
||||
- Search: Users are now findable by their urls.
|
||||
- Renamed `:await_up_timeout` in `:connections_pool` namespace to `:connect_timeout`, old name is deprecated.
|
||||
- Renamed `:timeout` in `pools` namespace to `:recv_timeout`, old name is deprecated.
|
||||
|
@ -47,7 +50,9 @@ switched to a new configuration mechanism, however it was not officially removed
|
|||
|
||||
- Add documented-but-missing chat pagination.
|
||||
- Allow sending out emails again.
|
||||
- Allow sending chat messages to yourself
|
||||
- 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)
|
||||
|
||||
|
|
|
@ -234,6 +234,7 @@ config :pleroma, :instance,
|
|||
"text/bbcode"
|
||||
],
|
||||
autofollowed_nicknames: [],
|
||||
autofollowing_nicknames: [],
|
||||
max_pinned_statuses: 1,
|
||||
attachment_links: false,
|
||||
max_report_comment_size: 1000,
|
||||
|
@ -635,7 +636,12 @@ config :pleroma, Pleroma.Emails.UserEmail,
|
|||
|
||||
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false
|
||||
|
||||
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
|
||||
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter,
|
||||
enabled: false,
|
||||
auth: false,
|
||||
ip_whitelist: [],
|
||||
path: "/api/pleroma/app_metrics",
|
||||
format: :text
|
||||
|
||||
config :pleroma, Pleroma.ScheduledActivity,
|
||||
daily_user_limit: 25,
|
||||
|
|
|
@ -831,6 +831,12 @@ config :pleroma, :config_description, [
|
|||
description:
|
||||
"Set to nicknames of (local) users that every new user should automatically follow"
|
||||
},
|
||||
%{
|
||||
key: :autofollowing_nicknames,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"Set to nicknames of (local) users that automatically follows every newly registered user"
|
||||
},
|
||||
%{
|
||||
key: :attachment_links,
|
||||
type: :boolean,
|
||||
|
@ -3716,5 +3722,42 @@ config :pleroma, :config_description, [
|
|||
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]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -2,15 +2,37 @@
|
|||
|
||||
Pleroma includes support for exporting metrics via the [prometheus_ex](https://github.com/deadtrickster/prometheus.ex) library.
|
||||
|
||||
Config example:
|
||||
|
||||
```
|
||||
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter,
|
||||
enabled: true,
|
||||
auth: {:basic, "myusername", "mypassword"},
|
||||
ip_whitelist: ["127.0.0.1"],
|
||||
path: "/api/pleroma/app_metrics",
|
||||
format: :text
|
||||
```
|
||||
|
||||
* `enabled` (Pleroma extension) enables the endpoint
|
||||
* `ip_whitelist` (Pleroma extension) could be used to restrict access only to specified IPs
|
||||
* `auth` sets the authentication (`false` for no auth; configurable to HTTP Basic Auth, see [prometheus-plugs](https://github.com/deadtrickster/prometheus-plugs#exporting) documentation)
|
||||
* `format` sets the output format (`:text` or `:protobuf`)
|
||||
* `path` sets the path to app metrics page
|
||||
|
||||
|
||||
## `/api/pleroma/app_metrics`
|
||||
|
||||
### Exports Prometheus application metrics
|
||||
|
||||
* Method: `GET`
|
||||
* Authentication: not required
|
||||
* Authentication: not required by default (see configuration options above)
|
||||
* Params: none
|
||||
* Response: JSON
|
||||
* Response: text
|
||||
|
||||
## Grafana
|
||||
|
||||
### Config example
|
||||
|
||||
The following is a config example to use with [Grafana](https://grafana.com)
|
||||
|
||||
```
|
||||
|
|
|
@ -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`
|
||||
field. Addressing multiple actors is not allowed. These messages are always
|
||||
private, there is no public version of them. They are created with a `Create`
|
||||
activity.
|
||||
|
||||
They are part of the `litepub` namespace as `http://litepub.social/ns#ChatMessage`.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
|
|
|
@ -7,97 +7,105 @@ Feel free to contact us to be added to this list!
|
|||
- Homepage: <https://www.pleroma.com/#desktopApp>
|
||||
- Source Code: <https://github.com/roma-apps/roma-desktop>
|
||||
- Platforms: Windows, Mac, Linux
|
||||
- Features: Streaming Ready
|
||||
- Features: MastoAPI, Streaming Ready
|
||||
|
||||
### Social
|
||||
- Source Code: <https://gitlab.gnome.org/World/Social>
|
||||
- Contact: [@brainblasted@social.libre.fi](https://social.libre.fi/users/brainblasted)
|
||||
- Platforms: Linux (GNOME)
|
||||
- Note(2019-01-28): Not at a pre-alpha stage yet
|
||||
- Features: MastoAPI
|
||||
|
||||
### Whalebird
|
||||
- Homepage: <https://whalebird.org/>
|
||||
- Source Code: <https://github.com/h3poteto/whalebird-desktop>
|
||||
- Contact: [@h3poteto@pleroma.io](https://pleroma.io/users/h3poteto)
|
||||
- Platforms: Windows, Mac, Linux
|
||||
- Features: Streaming Ready
|
||||
- Features: MastoAPI, Streaming Ready
|
||||
|
||||
## Handheld
|
||||
### AndStatus
|
||||
- Homepage: <http://andstatus.org/>
|
||||
- Source Code: <https://github.com/andstatus/andstatus/>
|
||||
- Platforms: Android
|
||||
- Features: MastoAPI, ActivityPub (Client-to-Server)
|
||||
|
||||
### Amaroq
|
||||
- Homepage: <https://itunes.apple.com/us/app/amaroq-for-mastodon/id1214116200>
|
||||
- Source Code: <https://github.com/ReticentJohn/Amaroq>
|
||||
- Contact: [@eurasierboy@mastodon.social](https://mastodon.social/users/eurasierboy)
|
||||
- Platforms: iOS
|
||||
- Features: No Streaming
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
### Fedilab
|
||||
- Homepage: <https://fedilab.app/>
|
||||
- Source Code: <https://framagit.org/tom79/fedilab/>
|
||||
- Contact: [@fedilab@framapiaf.org](https://framapiaf.org/users/fedilab)
|
||||
- Platforms: Android
|
||||
- Features: Streaming Ready, Moderation, Text Formatting
|
||||
- Features: MastoAPI, Streaming Ready, Moderation, Text Formatting
|
||||
|
||||
### Kyclos
|
||||
- Source Code: <https://git.pleroma.social/pleroma/harbour-kyclos>
|
||||
- Platforms: SailfishOS
|
||||
- Features: No Streaming
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
### Husky
|
||||
- Source code: <https://git.mentality.rip/FWGS/Husky>
|
||||
- Contact: [@Husky@enigmatic.observer](https://enigmatic.observer/users/Husky)
|
||||
- Platforms: Android
|
||||
- Features: No Streaming, Emoji Reactions, Text Formatting, FE Stickers
|
||||
- Features: MastoAPI, No Streaming, Emoji Reactions, Text Formatting, FE Stickers
|
||||
|
||||
### Fedi
|
||||
- Homepage: <https://www.fediapp.com/>
|
||||
- Source Code: Proprietary, but gratis
|
||||
- Platforms: iOS, Android
|
||||
- Features: Pleroma-specific features like Reactions
|
||||
- Features: MastoAPI, Pleroma-specific features like Reactions
|
||||
|
||||
### Tusky
|
||||
- Homepage: <https://tuskyapp.github.io/>
|
||||
- Source Code: <https://github.com/tuskyapp/Tusky>
|
||||
- Contact: [@ConnyDuck@mastodon.social](https://mastodon.social/users/ConnyDuck)
|
||||
- Platforms: Android
|
||||
- Features: No Streaming
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
### Twidere
|
||||
- Homepage: <https://twidere.mariotaku.org/>
|
||||
- Source Code: <https://github.com/TwidereProject/Twidere-Android/>
|
||||
- Contact: <me@mariotaku.org>
|
||||
- Platform: Android
|
||||
- Features: No Streaming
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
### Indigenous
|
||||
- Homepage: <https://indigenous.realize.be/>
|
||||
- Source Code: <https://github.com/swentel/indigenous-android/>
|
||||
- Contact: [@swentel@realize.be](https://realize.be)
|
||||
- Platforms: Android
|
||||
- Features: No Streaming
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
## Alternative Web Interfaces
|
||||
### Brutaldon
|
||||
- Homepage: <https://jfm.carcosa.net/projects/software/brutaldon/>
|
||||
- Source Code: <https://git.carcosa.net/jmcbray/brutaldon>
|
||||
- Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc)
|
||||
- Features: No Streaming
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
### Halcyon
|
||||
- Source Code: <https://notabug.org/halcyon-suite/halcyon>
|
||||
- Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon)
|
||||
- Features: Streaming Ready
|
||||
- Features: MastoAPI, Streaming Ready
|
||||
|
||||
### Pinafore
|
||||
- Homepage: <https://pinafore.social/>
|
||||
- Source Code: <https://github.com/nolanlawson/pinafore>
|
||||
- Contact: [@pinafore@mastodon.technology](https://mastodon.technology/users/pinafore)
|
||||
- Note: Pleroma support is a secondary goal
|
||||
- Features: No Streaming
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
### Sengi
|
||||
- Homepage: <https://nicolasconstant.github.io/sengi/>
|
||||
- Source Code: <https://github.com/NicolasConstant/sengi>
|
||||
- Contact: [@sengi_app@mastodon.social](https://mastodon.social/users/sengi_app)
|
||||
- Features: MastoAPI
|
||||
|
||||
### DashFE
|
||||
- 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/>
|
||||
- Contact: [@r@freesoftwareextremist.com](https://freesoftwareextremist.com/users/r)
|
||||
- Features: Does not requires JavaScript
|
||||
- Features: MastoAPI
|
||||
|
|
|
@ -45,6 +45,7 @@ To add configuration to your config file, you can copy it from the base config.
|
|||
older software for theses nicknames.
|
||||
* `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
|
||||
* `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.
|
||||
* `autofollowing_nicknames`: Set to nicknames of (local) users that automatically follows every newly registered user.
|
||||
* `attachment_links`: Set to true to enable automatically adding attachment link text to statuses.
|
||||
* `max_report_comment_size`: The maximum size of the report comment (Default: `1000`).
|
||||
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). Default: `false`.
|
||||
|
|
|
@ -43,7 +43,7 @@ Other than things bundled in the OTP release Pleroma depends on:
|
|||
|
||||
### 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
|
||||
* ffmpeg
|
||||
* exiftool
|
||||
|
|
|
@ -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
|
|
@ -426,7 +426,6 @@ defmodule Pleroma.User do
|
|||
params,
|
||||
[
|
||||
:bio,
|
||||
:name,
|
||||
:emoji,
|
||||
:ap_id,
|
||||
:inbox,
|
||||
|
@ -455,7 +454,9 @@ defmodule Pleroma.User do
|
|||
:accepts_chat_messages
|
||||
]
|
||||
)
|
||||
|> validate_required([:name, :ap_id])
|
||||
|> cast(params, [:name], empty_values: [])
|
||||
|> validate_required([:ap_id])
|
||||
|> validate_required([:name], trim: false)
|
||||
|> unique_constraint(:nickname)
|
||||
|> validate_format(:nickname, @email_regex)
|
||||
|> validate_length(:bio, max: bio_limit)
|
||||
|
@ -765,6 +766,16 @@ defmodule Pleroma.User do
|
|||
follow_all(user, autofollowed_users)
|
||||
end
|
||||
|
||||
defp autofollowing_users(user) do
|
||||
candidates = Config.get([:instance, :autofollowing_nicknames])
|
||||
|
||||
User.Query.build(%{nickname: candidates, local: true, deactivated: false})
|
||||
|> Repo.all()
|
||||
|> Enum.each(&follow(&1, user, :follow_accept))
|
||||
|
||||
{:ok, :success}
|
||||
end
|
||||
|
||||
@doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
|
||||
def register(%Ecto.Changeset{} = changeset) do
|
||||
with {:ok, user} <- Repo.insert(changeset) do
|
||||
|
@ -774,6 +785,7 @@ defmodule Pleroma.User do
|
|||
|
||||
def post_register_action(%User{} = user) do
|
||||
with {:ok, user} <- autofollow_users(user),
|
||||
{:ok, _} <- autofollowing_users(user),
|
||||
{:ok, user} <- set_cache(user),
|
||||
{:ok, _} <- send_welcome_email(user),
|
||||
{:ok, _} <- send_welcome_message(user),
|
||||
|
|
|
@ -1378,6 +1378,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
{:ok, data} <- user_data_from_user_object(data) do
|
||||
{:ok, maybe_update_follow_information(data)}
|
||||
else
|
||||
# If this has been deleted, only log a debug and not an error
|
||||
{:error, "Object has been deleted" = e} ->
|
||||
Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
|
||||
{:error, e}
|
||||
|
|
|
@ -525,19 +525,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
{new_user, for_user}
|
||||
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
|
||||
with {:ok, object} <-
|
||||
ActivityPub.upload(
|
||||
|
|
|
@ -44,29 +44,30 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
|||
def is_list?(%{data: %{"listMessage" => _}}), do: true
|
||||
def is_list?(_), do: false
|
||||
|
||||
@spec visible_for_user?(Activity.t(), User.t() | nil) :: boolean()
|
||||
def visible_for_user?(%{actor: ap_id}, %User{ap_id: ap_id}), do: true
|
||||
@spec visible_for_user?(Activity.t() | nil, User.t() | nil) :: boolean()
|
||||
def visible_for_user?(%Activity{actor: ap_id}, %User{ap_id: ap_id}), do: true
|
||||
|
||||
def visible_for_user?(nil, _), do: false
|
||||
|
||||
def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false
|
||||
def visible_for_user?(%Activity{data: %{"listMessage" => _}}, nil), do: false
|
||||
|
||||
def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{} = user) do
|
||||
def visible_for_user?(
|
||||
%Activity{data: %{"listMessage" => list_ap_id}} = activity,
|
||||
%User{} = user
|
||||
) do
|
||||
user.ap_id in activity.data["to"] ||
|
||||
list_ap_id
|
||||
|> Pleroma.List.get_by_ap_id()
|
||||
|> Pleroma.List.member?(user)
|
||||
end
|
||||
|
||||
def visible_for_user?(%{local: local} = activity, nil) do
|
||||
cfg_key = if local, do: :local, else: :remote
|
||||
|
||||
if Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key),
|
||||
def visible_for_user?(%Activity{} = activity, nil) do
|
||||
if restrict_unauthenticated_access?(activity),
|
||||
do: false,
|
||||
else: is_public?(activity)
|
||||
end
|
||||
|
||||
def visible_for_user?(activity, user) do
|
||||
def visible_for_user?(%Activity{} = activity, user) do
|
||||
x = [user.ap_id | User.following(user)]
|
||||
y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || [])
|
||||
is_public?(activity) || Enum.any?(x, &(&1 in y))
|
||||
|
@ -82,6 +83,26 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
|||
result
|
||||
end
|
||||
|
||||
def restrict_unauthenticated_access?(%Activity{local: local}) do
|
||||
restrict_unauthenticated_access_to_activity?(local)
|
||||
end
|
||||
|
||||
def restrict_unauthenticated_access?(%Object{} = object) do
|
||||
object
|
||||
|> Object.local?()
|
||||
|> restrict_unauthenticated_access_to_activity?()
|
||||
end
|
||||
|
||||
def restrict_unauthenticated_access?(%User{} = user) do
|
||||
User.visible_for(user, _reading_user = nil)
|
||||
end
|
||||
|
||||
defp restrict_unauthenticated_access_to_activity?(local?) when is_boolean(local?) do
|
||||
cfg_key = if local?, do: :local, else: :remote
|
||||
|
||||
Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key)
|
||||
end
|
||||
|
||||
def get_visibility(object) do
|
||||
to = object.data["to"] || []
|
||||
cc = object.data["cc"] || []
|
||||
|
|
|
@ -52,7 +52,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||
:skip_thread_containment,
|
||||
:pleroma_settings_store,
|
||||
:raw_fields,
|
||||
:discoverable,
|
||||
:is_discoverable,
|
||||
:actor_type
|
||||
])
|
||||
|> Map.merge(%{
|
||||
|
|
|
@ -7,6 +7,8 @@ defmodule Pleroma.Web.Endpoint do
|
|||
|
||||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.Config
|
||||
|
||||
socket("/socket", Pleroma.Web.UserSocket)
|
||||
|
||||
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
|
||||
|
@ -88,19 +90,19 @@ defmodule Pleroma.Web.Endpoint do
|
|||
plug(Plug.Parsers,
|
||||
parsers: [
|
||||
:urlencoded,
|
||||
{:multipart, length: {Pleroma.Config, :get, [[:instance, :upload_limit]]}},
|
||||
{:multipart, length: {Config, :get, [[:instance, :upload_limit]]}},
|
||||
:json
|
||||
],
|
||||
pass: ["*/*"],
|
||||
json_decoder: Jason,
|
||||
length: Pleroma.Config.get([:instance, :upload_limit]),
|
||||
length: Config.get([:instance, :upload_limit]),
|
||||
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
|
||||
)
|
||||
|
||||
plug(Plug.MethodOverride)
|
||||
plug(Plug.Head)
|
||||
|
||||
secure_cookies = Pleroma.Config.get([__MODULE__, :secure_cookie_flag])
|
||||
secure_cookies = Config.get([__MODULE__, :secure_cookie_flag])
|
||||
|
||||
cookie_name =
|
||||
if secure_cookies,
|
||||
|
@ -108,7 +110,7 @@ defmodule Pleroma.Web.Endpoint do
|
|||
else: "pleroma_key"
|
||||
|
||||
extra =
|
||||
Pleroma.Config.get([__MODULE__, :extra_cookie_attrs])
|
||||
Config.get([__MODULE__, :extra_cookie_attrs])
|
||||
|> Enum.join(";")
|
||||
|
||||
# The session will be stored in the cookie and signed,
|
||||
|
@ -118,7 +120,7 @@ defmodule Pleroma.Web.Endpoint do
|
|||
Plug.Session,
|
||||
store: :cookie,
|
||||
key: cookie_name,
|
||||
signing_salt: Pleroma.Config.get([__MODULE__, :signing_salt], "CqaoopA2"),
|
||||
signing_salt: Config.get([__MODULE__, :signing_salt], "CqaoopA2"),
|
||||
http_only: true,
|
||||
secure: secure_cookies,
|
||||
extra: extra
|
||||
|
@ -138,8 +140,34 @@ defmodule Pleroma.Web.Endpoint do
|
|||
use Prometheus.PlugExporter
|
||||
end
|
||||
|
||||
defmodule MetricsExporterCaller do
|
||||
@behaviour Plug
|
||||
|
||||
def init(opts), do: opts
|
||||
|
||||
def call(conn, opts) do
|
||||
prometheus_config = Application.get_env(:prometheus, MetricsExporter, [])
|
||||
ip_whitelist = List.wrap(prometheus_config[:ip_whitelist])
|
||||
|
||||
cond do
|
||||
!prometheus_config[:enabled] ->
|
||||
conn
|
||||
|
||||
ip_whitelist != [] and
|
||||
!Enum.find(ip_whitelist, fn ip ->
|
||||
Pleroma.Helpers.InetHelper.parse_address(ip) == {:ok, conn.remote_ip}
|
||||
end) ->
|
||||
conn
|
||||
|
||||
true ->
|
||||
MetricsExporter.call(conn, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
plug(PipelineInstrumenter)
|
||||
plug(MetricsExporter)
|
||||
|
||||
plug(MetricsExporterCaller)
|
||||
|
||||
plug(Pleroma.Web.Router)
|
||||
|
||||
|
|
|
@ -10,14 +10,14 @@ defmodule Pleroma.Web.Feed.TagController do
|
|||
alias Pleroma.Web.Feed.FeedView
|
||||
|
||||
def feed(conn, params) do
|
||||
unless Pleroma.Config.restrict_unauthenticated_access?(:activities, :local) do
|
||||
if Config.get!([:instance, :public]) do
|
||||
render_feed(conn, params)
|
||||
else
|
||||
render_error(conn, :not_found, "Not found")
|
||||
end
|
||||
end
|
||||
|
||||
def render_feed(conn, %{"tag" => raw_tag} = params) do
|
||||
defp render_feed(conn, %{"tag" => raw_tag} = params) do
|
||||
{format, tag} = parse_tag(raw_tag)
|
||||
|
||||
activities =
|
||||
|
@ -36,12 +36,13 @@ defmodule Pleroma.Web.Feed.TagController do
|
|||
end
|
||||
|
||||
@spec parse_tag(binary() | any()) :: {format :: String.t(), tag :: String.t()}
|
||||
defp parse_tag(raw_tag) when is_binary(raw_tag) do
|
||||
case Enum.reverse(String.split(raw_tag, ".")) do
|
||||
[format | tag] when format in ["atom", "rss"] -> {format, Enum.join(tag, ".")}
|
||||
_ -> {"rss", raw_tag}
|
||||
defp parse_tag(raw_tag) do
|
||||
case is_binary(raw_tag) && Enum.reverse(String.split(raw_tag, ".")) do
|
||||
[format | tag] when format in ["rss", "atom"] ->
|
||||
{format, Enum.join(tag, ".")}
|
||||
|
||||
_ ->
|
||||
{"atom", raw_tag}
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_tag(raw_tag), do: {"rss", raw_tag}
|
||||
end
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.Feed.UserController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.ActivityPubController
|
||||
|
@ -22,12 +23,7 @@ defmodule Pleroma.Web.Feed.UserController do
|
|||
|
||||
def feed_redirect(%{assigns: %{format: format}} = conn, _params)
|
||||
when format in ["json", "activity+json"] do
|
||||
with %{halted: false} = conn <-
|
||||
Pleroma.Web.Plugs.EnsureAuthenticatedPlug.call(conn,
|
||||
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
|
||||
) do
|
||||
ActivityPubController.call(conn, :user)
|
||||
end
|
||||
ActivityPubController.call(conn, :user)
|
||||
end
|
||||
|
||||
def feed_redirect(conn, %{"nickname" => nickname}) do
|
||||
|
@ -36,25 +32,18 @@ defmodule Pleroma.Web.Feed.UserController do
|
|||
end
|
||||
end
|
||||
|
||||
def feed(conn, params) do
|
||||
unless Pleroma.Config.restrict_unauthenticated_access?(:profiles, :local) do
|
||||
render_feed(conn, params)
|
||||
else
|
||||
errors(conn, {:error, :not_found})
|
||||
end
|
||||
end
|
||||
|
||||
def render_feed(conn, %{"nickname" => nickname} = params) do
|
||||
def feed(conn, %{"nickname" => nickname} = params) do
|
||||
format = get_format(conn)
|
||||
|
||||
format =
|
||||
if format in ["rss", "atom"] do
|
||||
if format in ["atom", "rss"] do
|
||||
format
|
||||
else
|
||||
"atom"
|
||||
end
|
||||
|
||||
with {_, %User{local: true} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
|
||||
with {_, %User{local: true} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)},
|
||||
{_, :visible} <- {:visibility, User.visible_for(user, _reading_user = nil)} do
|
||||
activities =
|
||||
%{
|
||||
type: ["Create"],
|
||||
|
@ -69,7 +58,7 @@ defmodule Pleroma.Web.Feed.UserController do
|
|||
|> render("user.#{format}",
|
||||
user: user,
|
||||
activities: activities,
|
||||
feed_config: Pleroma.Config.get([:feed])
|
||||
feed_config: Config.get([:feed])
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -81,6 +70,8 @@ defmodule Pleroma.Web.Feed.UserController do
|
|||
def errors(conn, {:fetch_user, %User{local: false}}), do: errors(conn, {:error, :not_found})
|
||||
def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
|
||||
|
||||
def errors(conn, {:visibility, _}), do: errors(conn, {:error, :not_found})
|
||||
|
||||
def errors(conn, _) do
|
||||
render_error(conn, :internal_server_error, "Something went wrong")
|
||||
end
|
||||
|
|
|
@ -16,10 +16,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
alias Pleroma.Web.Plugs.RateLimiter
|
||||
alias Pleroma.Web.Router
|
||||
|
||||
plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug,
|
||||
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
|
||||
)
|
||||
|
||||
plug(
|
||||
RateLimiter,
|
||||
[name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
|
||||
|
@ -37,14 +33,12 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
ActivityPubController.call(conn, :object)
|
||||
end
|
||||
|
||||
def object(%{assigns: %{format: format}} = conn, _params) do
|
||||
def object(conn, _params) do
|
||||
with id <- Endpoint.url() <> conn.request_path,
|
||||
{_, %Activity{} = activity} <-
|
||||
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
|
||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||
case format do
|
||||
_ -> redirect(conn, to: "/notice/#{activity.id}")
|
||||
end
|
||||
redirect(conn, to: "/notice/#{activity.id}")
|
||||
else
|
||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||
{:error, :not_found}
|
||||
|
@ -59,13 +53,11 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
ActivityPubController.call(conn, :activity)
|
||||
end
|
||||
|
||||
def activity(%{assigns: %{format: format}} = conn, _params) do
|
||||
def activity(conn, _params) do
|
||||
with id <- Endpoint.url() <> conn.request_path,
|
||||
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
|
||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||
case format do
|
||||
_ -> redirect(conn, to: "/notice/#{activity.id}")
|
||||
end
|
||||
redirect(conn, to: "/notice/#{activity.id}")
|
||||
else
|
||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||
{:error, :not_found}
|
||||
|
@ -119,6 +111,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||
def notice_player(conn, %{"id" => id}) do
|
||||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
|
||||
true <- Visibility.is_public?(activity),
|
||||
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
|
||||
%Object{} = object <- Object.normalize(activity),
|
||||
%{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
|
||||
true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
|
||||
|
|
|
@ -34,22 +34,26 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do
|
|||
end
|
||||
|
||||
def call(conn, opts) do
|
||||
frontend_type = Map.get(opts, :frontend_type, :primary)
|
||||
path = file_path("", frontend_type)
|
||||
|
||||
if path do
|
||||
conn
|
||||
|> call_static(opts, path)
|
||||
with false <- invalid_path?(conn.path_info),
|
||||
frontend_type <- Map.get(opts, :frontend_type, :primary),
|
||||
path when not is_nil(path) <- file_path("", frontend_type) do
|
||||
call_static(conn, opts, path)
|
||||
else
|
||||
conn
|
||||
_ ->
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
defp call_static(conn, opts, from) do
|
||||
opts =
|
||||
opts
|
||||
|> Map.put(:from, from)
|
||||
defp invalid_path?(list) do
|
||||
invalid_path?(list, :binary.compile_pattern(["/", "\\", ":", "\0"]))
|
||||
end
|
||||
|
||||
defp invalid_path?([h | _], _match) when h in [".", "..", ""], do: true
|
||||
defp invalid_path?([h | t], match), do: String.contains?(h, match) or invalid_path?(t)
|
||||
defp invalid_path?([], _match), do: false
|
||||
|
||||
defp call_static(conn, opts, from) do
|
||||
opts = Map.put(opts, :from, from)
|
||||
Plug.Static.call(conn, opts)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,6 +5,26 @@
|
|||
defmodule Pleroma.Web.Router do
|
||||
use Pleroma.Web, :router
|
||||
|
||||
pipeline :accepts_html do
|
||||
plug(:accepts, ["html"])
|
||||
end
|
||||
|
||||
pipeline :accepts_html_xml do
|
||||
plug(:accepts, ["html", "xml", "rss", "atom"])
|
||||
end
|
||||
|
||||
pipeline :accepts_html_json do
|
||||
plug(:accepts, ["html", "activity+json", "json"])
|
||||
end
|
||||
|
||||
pipeline :accepts_html_xml_json do
|
||||
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
|
||||
end
|
||||
|
||||
pipeline :accepts_xml_rss_atom do
|
||||
plug(:accepts, ["xml", "rss", "atom"])
|
||||
end
|
||||
|
||||
pipeline :browser do
|
||||
plug(:accepts, ["html"])
|
||||
plug(:fetch_session)
|
||||
|
@ -566,30 +586,43 @@ defmodule Pleroma.Web.Router do
|
|||
)
|
||||
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
|
||||
pipe_through([:ostatus, :http_signature])
|
||||
# Note: html format is supported only if static FE is enabled
|
||||
# Note: http signature is only considered for json requests (no auth for non-json requests)
|
||||
pipe_through([:accepts_html_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
|
||||
|
||||
get("/objects/:uuid", OStatus.OStatusController, :object)
|
||||
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
||||
get("/notice/:id", OStatus.OStatusController, :notice)
|
||||
get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)
|
||||
|
||||
# Mastodon compatibility routes
|
||||
get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
|
||||
get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
# Note: html format is supported only if static FE is enabled
|
||||
# Note: http signature is only considered for json requests (no auth for non-json requests)
|
||||
pipe_through([:accepts_html_xml_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
|
||||
|
||||
# Note: returns user _profile_ for json requests, redirects to user _feed_ for non-json ones
|
||||
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
# Note: html format is supported only if static FE is enabled
|
||||
pipe_through([:accepts_html_xml, Pleroma.Web.Plugs.StaticFEPlug])
|
||||
|
||||
get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
|
||||
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
pipe_through(:accepts_html)
|
||||
get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
pipe_through(:accepts_xml_rss_atom)
|
||||
get("/tags/:tag", Feed.TagController, :feed, as: :tag_feed)
|
||||
end
|
||||
|
||||
|
|
|
@ -17,12 +17,96 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
|||
plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)
|
||||
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"]
|
||||
|
||||
@doc "Renders requested local public activity or public activities of requested user"
|
||||
def show(%{assigns: %{notice_id: notice_id}} = conn, _params) do
|
||||
with %Activity{local: true} = activity <-
|
||||
Activity.get_by_id_with_object(notice_id),
|
||||
true <- Visibility.is_public?(activity.object),
|
||||
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
|
||||
%User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do
|
||||
meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user})
|
||||
|
||||
timeline =
|
||||
activity.object.data["context"]
|
||||
|> ActivityPub.fetch_activities_for_context(%{})
|
||||
|> Enum.reverse()
|
||||
|> Enum.map(&represent(&1, &1.object.id == activity.object.id))
|
||||
|
||||
render(conn, "conversation.html", %{activities: timeline, meta: meta})
|
||||
else
|
||||
%Activity{object: %Object{data: data}} ->
|
||||
conn
|
||||
|> put_status(:found)
|
||||
|> redirect(external: data["url"] || data["external_url"] || data["id"])
|
||||
|
||||
_ ->
|
||||
not_found(conn, "Post not found.")
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do
|
||||
with {_, %User{local: true} = user} <-
|
||||
{:fetch_user, User.get_cached_by_nickname_or_id(username_or_id)},
|
||||
{_, :visible} <- {:visibility, User.visible_for(user, _reading_user = nil)} do
|
||||
meta = Metadata.build_tags(%{user: user})
|
||||
|
||||
params =
|
||||
params
|
||||
|> Map.take(@page_keys)
|
||||
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
|
||||
|
||||
timeline =
|
||||
user
|
||||
|> ActivityPub.fetch_user_activities(_reading_user = nil, params)
|
||||
|> Enum.map(&represent/1)
|
||||
|
||||
prev_page_id =
|
||||
(params["min_id"] || params["max_id"]) &&
|
||||
List.first(timeline) && List.first(timeline).id
|
||||
|
||||
next_page_id = List.last(timeline) && List.last(timeline).id
|
||||
|
||||
render(conn, "profile.html", %{
|
||||
user: User.sanitize_html(user),
|
||||
timeline: timeline,
|
||||
prev_page_id: prev_page_id,
|
||||
next_page_id: next_page_id,
|
||||
meta: meta
|
||||
})
|
||||
else
|
||||
_ ->
|
||||
not_found(conn, "User not found.")
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{object_id: _}} = conn, _params) do
|
||||
url = Helpers.url(conn) <> conn.request_path
|
||||
|
||||
case Activity.get_create_by_object_ap_id_with_object(url) do
|
||||
%Activity{} = activity ->
|
||||
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
||||
redirect(conn, to: to)
|
||||
|
||||
_ ->
|
||||
not_found(conn, "Post not found.")
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{activity_id: _}} = conn, _params) do
|
||||
url = Helpers.url(conn) <> conn.request_path
|
||||
|
||||
case Activity.get_by_ap_id(url) do
|
||||
%Activity{} = activity ->
|
||||
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
||||
redirect(conn, to: to)
|
||||
|
||||
_ ->
|
||||
not_found(conn, "Post not found.")
|
||||
end
|
||||
end
|
||||
|
||||
defp get_title(%Object{data: %{"name" => name}}) when is_binary(name),
|
||||
do: name
|
||||
|
||||
|
@ -81,91 +165,6 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
|||
}
|
||||
end
|
||||
|
||||
def show(%{assigns: %{notice_id: notice_id}} = conn, _params) do
|
||||
with %Activity{local: true} = activity <-
|
||||
Activity.get_by_id_with_object(notice_id),
|
||||
true <- Visibility.is_public?(activity.object),
|
||||
%User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do
|
||||
meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user})
|
||||
|
||||
timeline =
|
||||
activity.object.data["context"]
|
||||
|> ActivityPub.fetch_activities_for_context(%{})
|
||||
|> Enum.reverse()
|
||||
|> Enum.map(&represent(&1, &1.object.id == activity.object.id))
|
||||
|
||||
render(conn, "conversation.html", %{activities: timeline, meta: meta})
|
||||
else
|
||||
%Activity{object: %Object{data: data}} ->
|
||||
conn
|
||||
|> put_status(:found)
|
||||
|> redirect(external: data["url"] || data["external_url"] || data["id"])
|
||||
|
||||
_ ->
|
||||
not_found(conn, "Post not found.")
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do
|
||||
case User.get_cached_by_nickname_or_id(username_or_id) do
|
||||
%User{} = user ->
|
||||
meta = Metadata.build_tags(%{user: user})
|
||||
|
||||
params =
|
||||
params
|
||||
|> Map.take(@page_keys)
|
||||
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
|
||||
|
||||
timeline =
|
||||
user
|
||||
|> ActivityPub.fetch_user_activities(nil, params)
|
||||
|> Enum.map(&represent/1)
|
||||
|
||||
prev_page_id =
|
||||
(params["min_id"] || params["max_id"]) &&
|
||||
List.first(timeline) && List.first(timeline).id
|
||||
|
||||
next_page_id = List.last(timeline) && List.last(timeline).id
|
||||
|
||||
render(conn, "profile.html", %{
|
||||
user: User.sanitize_html(user),
|
||||
timeline: timeline,
|
||||
prev_page_id: prev_page_id,
|
||||
next_page_id: next_page_id,
|
||||
meta: meta
|
||||
})
|
||||
|
||||
_ ->
|
||||
not_found(conn, "User not found.")
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{object_id: _}} = conn, _params) do
|
||||
url = Helpers.url(conn) <> conn.request_path
|
||||
|
||||
case Activity.get_create_by_object_ap_id_with_object(url) do
|
||||
%Activity{} = activity ->
|
||||
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
||||
redirect(conn, to: to)
|
||||
|
||||
_ ->
|
||||
not_found(conn, "Post not found.")
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{activity_id: _}} = conn, _params) do
|
||||
url = Helpers.url(conn) <> conn.request_path
|
||||
|
||||
case Activity.get_by_ap_id(url) do
|
||||
%Activity{} = activity ->
|
||||
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
||||
redirect(conn, to: to)
|
||||
|
||||
_ ->
|
||||
not_found(conn, "Post not found.")
|
||||
end
|
||||
end
|
||||
|
||||
defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
|
||||
do: assign(conn, :notice_id, notice_id)
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -388,6 +388,7 @@ defmodule Pleroma.UserTest do
|
|||
}
|
||||
|
||||
setup do: clear_config([:instance, :autofollowed_nicknames])
|
||||
setup do: clear_config([:instance, :autofollowing_nicknames])
|
||||
setup do: clear_config([:welcome])
|
||||
setup do: clear_config([:instance, :account_activation_required])
|
||||
|
||||
|
@ -408,6 +409,23 @@ defmodule Pleroma.UserTest do
|
|||
refute User.following?(registered_user, remote_user)
|
||||
end
|
||||
|
||||
test "it adds automatic followers for new registered accounts" do
|
||||
user1 = insert(:user)
|
||||
user2 = insert(:user)
|
||||
|
||||
Pleroma.Config.put([:instance, :autofollowing_nicknames], [
|
||||
user1.nickname,
|
||||
user2.nickname
|
||||
])
|
||||
|
||||
cng = User.register_changeset(%User{}, @full_user_data)
|
||||
|
||||
{:ok, registered_user} = User.register(cng)
|
||||
|
||||
assert User.following?(user1, registered_user)
|
||||
assert User.following?(user2, registered_user)
|
||||
end
|
||||
|
||||
test "it sends a welcome message if it is set" do
|
||||
welcome_user = insert(:user)
|
||||
Pleroma.Config.put([:welcome, :direct_message, :enabled], true)
|
||||
|
|
|
@ -156,21 +156,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert response == "Not found"
|
||||
end
|
||||
|
||||
test "it requires authentication if instance is NOT federating", %{
|
||||
conn: conn
|
||||
} do
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
put_req_header(
|
||||
conn,
|
||||
"accept",
|
||||
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
|
||||
)
|
||||
|
||||
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}.json", user)
|
||||
end
|
||||
end
|
||||
|
||||
describe "mastodon compatibility routes" do
|
||||
|
@ -338,18 +323,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert "Not found" == json_response(conn2, :not_found)
|
||||
end
|
||||
|
||||
test "it requires authentication if instance is NOT federating", %{
|
||||
conn: conn
|
||||
} do
|
||||
user = insert(:user)
|
||||
note = insert(:note)
|
||||
uuid = String.split(note.data["id"], "/") |> List.last()
|
||||
|
||||
conn = put_req_header(conn, "accept", "application/activity+json")
|
||||
|
||||
ensure_federating_or_authenticated(conn, "/objects/#{uuid}", user)
|
||||
end
|
||||
end
|
||||
|
||||
describe "/activities/:uuid" do
|
||||
|
@ -421,18 +394,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert "Not found" == json_response(conn2, :not_found)
|
||||
end
|
||||
|
||||
test "it requires authentication if instance is NOT federating", %{
|
||||
conn: conn
|
||||
} do
|
||||
user = insert(:user)
|
||||
activity = insert(:note_activity)
|
||||
uuid = String.split(activity.data["id"], "/") |> List.last()
|
||||
|
||||
conn = put_req_header(conn, "accept", "application/activity+json")
|
||||
|
||||
ensure_federating_or_authenticated(conn, "/activities/#{uuid}", user)
|
||||
end
|
||||
end
|
||||
|
||||
describe "/inbox" do
|
||||
|
@ -893,15 +854,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert response(conn, 200) =~ announce_activity.data["object"]
|
||||
end
|
||||
|
||||
test "it requires authentication if instance is NOT federating", %{
|
||||
conn: conn
|
||||
} do
|
||||
user = insert(:user)
|
||||
conn = put_req_header(conn, "accept", "application/activity+json")
|
||||
|
||||
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}/outbox", user)
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /users/:nickname/outbox (C2S)" do
|
||||
|
|
|
@ -2273,4 +2273,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
assert length(activities) == 2
|
||||
end
|
||||
end
|
||||
|
||||
test "allow fetching of accounts with an empty string name field" do
|
||||
Tesla.Mock.mock(fn
|
||||
%{method: :get, url: "https://princess.cat/users/mewmew"} ->
|
||||
file = File.read!("test/fixtures/mewmew_no_name.json")
|
||||
%Tesla.Env{status: 200, body: file}
|
||||
end)
|
||||
|
||||
{:ok, user} = ActivityPub.make_user_from_ap_id("https://princess.cat/users/mewmew")
|
||||
assert user.name == " "
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|
|||
import Pleroma.Factory
|
||||
import SweetXml
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Feed.FeedView
|
||||
|
@ -15,7 +16,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|
|||
setup do: clear_config([:feed])
|
||||
|
||||
test "gets a feed (ATOM)", %{conn: conn} do
|
||||
Pleroma.Config.put(
|
||||
Config.put(
|
||||
[:feed, :post_title],
|
||||
%{max_length: 25, omission: "..."}
|
||||
)
|
||||
|
@ -82,7 +83,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|
|||
end
|
||||
|
||||
test "gets a feed (RSS)", %{conn: conn} do
|
||||
Pleroma.Config.put(
|
||||
Config.put(
|
||||
[:feed, :post_title],
|
||||
%{max_length: 25, omission: "..."}
|
||||
)
|
||||
|
@ -157,7 +158,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|
|||
response =
|
||||
conn
|
||||
|> put_req_header("accept", "application/rss+xml")
|
||||
|> get(tag_feed_path(conn, :feed, "pleromaart"))
|
||||
|> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|
||||
|> response(200)
|
||||
|
||||
xml = parse(response)
|
||||
|
@ -183,14 +184,12 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|
|||
end
|
||||
|
||||
describe "private instance" do
|
||||
setup do: clear_config([:instance, :public])
|
||||
setup do: clear_config([:instance, :public], false)
|
||||
|
||||
test "returns 404 for tags feed", %{conn: conn} do
|
||||
Config.put([:instance, :public], false)
|
||||
|
||||
conn
|
||||
|> put_req_header("accept", "application/rss+xml")
|
||||
|> get(tag_feed_path(conn, :feed, "pleromaart"))
|
||||
|> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|
||||
|> response(404)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
setup do: clear_config([:instance, :federating], true)
|
||||
setup do: clear_config([:static_fe, :enabled], false)
|
||||
|
||||
describe "feed" do
|
||||
setup do: clear_config([:feed])
|
||||
|
@ -192,6 +192,16 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
|||
|> get(user_feed_path(conn, :feed, user.nickname))
|
||||
|> response(404)
|
||||
end
|
||||
|
||||
test "does not require authentication on non-federating instances", %{conn: conn} do
|
||||
clear_config([:instance, :federating], false)
|
||||
user = insert(:user)
|
||||
|
||||
conn
|
||||
|> put_req_header("accept", "application/rss+xml")
|
||||
|> get("/users/#{user.nickname}/feed.rss")
|
||||
|> response(200)
|
||||
end
|
||||
end
|
||||
|
||||
# Note: see ActivityPubControllerTest for JSON format tests
|
||||
|
|
|
@ -7,7 +7,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
|||
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
@ -21,7 +20,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
|||
:ok
|
||||
end
|
||||
|
||||
setup do: clear_config([:instance, :federating], true)
|
||||
setup do: clear_config([:static_fe, :enabled], false)
|
||||
|
||||
describe "Mastodon compatibility routes" do
|
||||
setup %{conn: conn} do
|
||||
|
@ -215,15 +214,16 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
|||
assert response(conn, 404)
|
||||
end
|
||||
|
||||
test "it requires authentication if instance is NOT federating", %{
|
||||
test "does not require authentication on non-federating instances", %{
|
||||
conn: conn
|
||||
} do
|
||||
user = insert(:user)
|
||||
clear_config([:instance, :federating], false)
|
||||
note_activity = insert(:note_activity)
|
||||
|
||||
conn = put_req_header(conn, "accept", "text/html")
|
||||
|
||||
ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}", user)
|
||||
conn
|
||||
|> put_req_header("accept", "text/html")
|
||||
|> get("/notice/#{note_activity.id}")
|
||||
|> response(200)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -325,14 +325,16 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
|||
|> response(404)
|
||||
end
|
||||
|
||||
test "it requires authentication if instance is NOT federating", %{
|
||||
test "does not require authentication on non-federating instances", %{
|
||||
conn: conn,
|
||||
note_activity: note_activity
|
||||
} do
|
||||
user = insert(:user)
|
||||
conn = put_req_header(conn, "accept", "text/html")
|
||||
clear_config([:instance, :federating], false)
|
||||
|
||||
ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}/embed_player", user)
|
||||
conn
|
||||
|> put_req_header("accept", "text/html")
|
||||
|> get("/notice/#{note_activity.id}/embed_player")
|
||||
|> response(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
import Mock
|
||||
|
||||
@dir "test/tmp/instance_static"
|
||||
|
||||
|
@ -53,4 +54,24 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
|
|||
index = get(conn, "/pleroma/admin/")
|
||||
assert html_response(index, 200) == "from frontend plug"
|
||||
end
|
||||
|
||||
test "exclude invalid path", %{conn: conn} do
|
||||
name = "pleroma-fe"
|
||||
ref = "dist"
|
||||
clear_config([:media_proxy, :enabled], true)
|
||||
clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
|
||||
clear_config([:frontends, :primary], %{"name" => name, "ref" => ref})
|
||||
path = "#{@dir}/frontends/#{name}/#{ref}"
|
||||
|
||||
File.mkdir_p!("#{path}/proxy/rr/ss")
|
||||
File.write!("#{path}/proxy/rr/ss/Ek7w8WPVcAApOvN.jpg:large", "FB image")
|
||||
|
||||
url =
|
||||
Pleroma.Web.MediaProxy.encode_url("https://pbs.twimg.com/media/Ek7w8WPVcAApOvN.jpg:large")
|
||||
|
||||
with_mock Pleroma.ReverseProxy,
|
||||
call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do
|
||||
assert %Plug.Conn{status: :success} = get(conn, url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,14 +6,12 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
|
|||
use Pleroma.Web.ConnCase
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
setup_all do: clear_config([:static_fe, :enabled], true)
|
||||
setup do: clear_config([:instance, :federating], true)
|
||||
|
||||
setup %{conn: conn} do
|
||||
conn = put_req_header(conn, "accept", "text/html")
|
||||
|
@ -74,8 +72,27 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
|
|||
refute html =~ ">test29<"
|
||||
end
|
||||
|
||||
test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do
|
||||
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}", user)
|
||||
test "does not require authentication on non-federating instances", %{
|
||||
conn: conn,
|
||||
user: user
|
||||
} do
|
||||
clear_config([:instance, :federating], false)
|
||||
|
||||
conn = get(conn, "/users/#{user.nickname}")
|
||||
|
||||
assert html_response(conn, 200) =~ user.nickname
|
||||
end
|
||||
|
||||
test "returns 404 for local user with `restrict_unauthenticated/profiles/local` setting", %{
|
||||
conn: conn
|
||||
} do
|
||||
clear_config([:restrict_unauthenticated, :profiles, :local], true)
|
||||
|
||||
local_user = insert(:user, local: true)
|
||||
|
||||
conn
|
||||
|> get("/users/#{local_user.nickname}")
|
||||
|> html_response(404)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -187,10 +204,28 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
|
|||
assert html_response(conn, 302) =~ "redirected"
|
||||
end
|
||||
|
||||
test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do
|
||||
test "does not require authentication on non-federating instances", %{
|
||||
conn: conn,
|
||||
user: user
|
||||
} do
|
||||
clear_config([:instance, :federating], false)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"})
|
||||
|
||||
ensure_federating_or_authenticated(conn, "/notice/#{activity.id}", user)
|
||||
conn = get(conn, "/notice/#{activity.id}")
|
||||
|
||||
assert html_response(conn, 200) =~ "testing a thing!"
|
||||
end
|
||||
|
||||
test "returns 404 for local public activity with `restrict_unauthenticated/activities/local` setting",
|
||||
%{conn: conn, user: user} do
|
||||
clear_config([:restrict_unauthenticated, :activities, :local], true)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"})
|
||||
|
||||
conn
|
||||
|> get("/notice/#{activity.id}")
|
||||
|> html_response(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -112,28 +112,6 @@ defmodule Pleroma.Web.ConnCase do
|
|||
defp json_response_and_validate_schema(conn, _status) do
|
||||
flunk("Response schema not found for #{conn.method} #{conn.request_path} #{conn.status}")
|
||||
end
|
||||
|
||||
defp ensure_federating_or_authenticated(conn, url, user) do
|
||||
initial_setting = Config.get([:instance, :federating])
|
||||
on_exit(fn -> Config.put([:instance, :federating], initial_setting) end)
|
||||
|
||||
Config.put([:instance, :federating], false)
|
||||
|
||||
conn
|
||||
|> get(url)
|
||||
|> response(403)
|
||||
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get(url)
|
||||
|> response(200)
|
||||
|
||||
Config.put([:instance, :federating], true)
|
||||
|
||||
conn
|
||||
|> get(url)
|
||||
|> response(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue