Merge remote-tracking branch 'remotes/origin/develop' into 1559-follow-request-notifications

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
Ivan Tashkinov 2020-04-17 16:23:58 +03:00
commit 6e6f95c6ae
128 changed files with 2622 additions and 880 deletions

View file

@ -4,9 +4,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [unreleased] ## [unreleased]
### Changed
- **Breaking:** BBCode and Markdown formatters will no longer return any `\n` and only use `<br/>` for newlines
### Removed ### Removed
- **Breaking:** removed `with_move` parameter from notifications timeline. - **Breaking:** removed `with_move` parameter from notifications timeline.
@ -15,15 +12,73 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- NodeInfo: `pleroma_emoji_reactions` to the `features` list. - NodeInfo: `pleroma_emoji_reactions` to the `features` list.
- Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses. - Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma wont start. For hackney OTP update is not required. - New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma wont start. For hackney OTP update is not required.
- Mix task to create trusted OAuth App.
- Notifications: Added `follow_request` notification type (configurable, see `[:notifications, :enable_follow_request_notifications]` setting). - Notifications: Added `follow_request` notification type (configurable, see `[:notifications, :enable_follow_request_notifications]` setting).
<details> <details>
<summary>API Changes</summary> <summary>API Changes</summary>
- Mastodon API: Support for `include_types` in `/api/v1/notifications`. - Mastodon API: Support for `include_types` in `/api/v1/notifications`.
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.
- Admin API: endpoints for create/update/delete OAuth Apps.
</details> </details>
### Fixed
- Support pagination in conversations API
## [unreleased-patch]
### Fixed
- Logger configuration through AdminFE
### Added
<details>
<summary>API Changes</summary>
- Admin API: `GET /api/pleroma/admin/need_reboot`.
</details>
## [2.0.2] - 2020-04-08
### Added
- Support for Funkwhale's `Audio` activity
- Admin API: `PATCH /api/pleroma/admin/users/:nickname/update_credentials`
### Fixed
- Blocked/muted users still generating push notifications
- Input textbox for bio ignoring newlines
- OTP: Inability to use PostgreSQL databases with SSL
- `user delete_activities` breaking when trying to delete already deleted posts
- Incorrect URL for Funkwhale channels
### Upgrade notes
1. Restart Pleroma
## [2.0.1] - 2020-03-15
### Security
- Static-FE: Fix remote posts not being sanitized
### Fixed
- 500 errors when no `Accept` header is present if Static-FE is enabled
- Instance panel not being updated immediately due to wrong `Cache-Control` headers
- Statuses posted with BBCode/Markdown having unncessary newlines in Pleroma-FE
- OTP: Fix some settings not being migrated to in-database config properly
- No `Cache-Control` headers on attachment/media proxy requests
- Character limit enforcement being off by 1
- Mastodon Streaming API: hashtag timelines not working
### Changed
- BBCode and Markdown formatters will no longer return any `\n` and only use `<br/>` for newlines
- Mastodon API: Allow registration without email if email verification is not enabled
### Upgrade notes
#### Nginx only
1. Remove `proxy_ignore_headers Cache-Control;` and `proxy_hide_header Cache-Control;` from your config.
#### Everyone
1. Run database migrations (inside Pleroma directory):
- OTP: `./bin/pleroma_ctl migrate`
- From Source: `mix ecto.migrate`
2. Restart Pleroma
## [2.0.0] - 2019-03-08 ## [2.0.0] - 2019-03-08
### Security ### Security
- Mastodon API: Fix being able to request enourmous amount of statuses in timelines leading to DoS. Now limited to 40 per request. - Mastodon API: Fix being able to request enormous amount of statuses in timelines leading to DoS. Now limited to 40 per request.
### Removed ### Removed
- **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media` - **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media`

View file

@ -386,47 +386,56 @@ defmodule Pleroma.LoadTesting.Fetcher do
favourites = ActivityPub.fetch_favourites(user) favourites = ActivityPub.fetch_favourites(user)
output_relationships =
!!Pleroma.Config.get([:extensions, :output_relationships_in_statuses_by_default])
Benchee.run( Benchee.run(
%{ %{
"Rendering home timeline" => fn -> "Rendering home timeline" => fn ->
StatusView.render("index.json", %{ StatusView.render("index.json", %{
activities: home_activities, activities: home_activities,
for: user, for: user,
as: :activity as: :activity,
skip_relationships: !output_relationships
}) })
end, end,
"Rendering direct timeline" => fn -> "Rendering direct timeline" => fn ->
StatusView.render("index.json", %{ StatusView.render("index.json", %{
activities: direct_activities, activities: direct_activities,
for: user, for: user,
as: :activity as: :activity,
skip_relationships: !output_relationships
}) })
end, end,
"Rendering public timeline" => fn -> "Rendering public timeline" => fn ->
StatusView.render("index.json", %{ StatusView.render("index.json", %{
activities: public_activities, activities: public_activities,
for: user, for: user,
as: :activity as: :activity,
skip_relationships: !output_relationships
}) })
end, end,
"Rendering tag timeline" => fn -> "Rendering tag timeline" => fn ->
StatusView.render("index.json", %{ StatusView.render("index.json", %{
activities: tag_activities, activities: tag_activities,
for: user, for: user,
as: :activity as: :activity,
skip_relationships: !output_relationships
}) })
end, end,
"Rendering notifications" => fn -> "Rendering notifications" => fn ->
Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{ Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
notifications: notifications, notifications: notifications,
for: user for: user,
skip_relationships: !output_relationships
}) })
end, end,
"Rendering favourites timeline" => fn -> "Rendering favourites timeline" => fn ->
StatusView.render("index.json", %{ StatusView.render("index.json", %{
activities: favourites, activities: favourites,
for: user, for: user,
as: :activity as: :activity,
skip_relationships: !output_relationships
}) })
end end
}, },

View file

@ -240,6 +240,8 @@ config :pleroma, :instance,
extended_nickname_format: true, extended_nickname_format: true,
cleanup_attachments: false cleanup_attachments: false
config :pleroma, :extensions, output_relationships_in_statuses_by_default: true
config :pleroma, :feed, config :pleroma, :feed,
post_title: %{ post_title: %{
max_length: 100, max_length: 100,

View file

@ -392,6 +392,19 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- `email` - `email`
- `name`, optional - `name`, optional
- Response:
- On success: `204`, empty response
- On failure:
- 400 Bad Request, JSON:
```json
[
{
"error": "Appropriate error message here"
}
]
```
## `GET /api/pleroma/admin/users/:nickname/password_reset` ## `GET /api/pleroma/admin/users/:nickname/password_reset`
### Get a password reset token for a given nickname ### Get a password reset token for a given nickname
@ -773,6 +786,8 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
### Restarts pleroma application ### Restarts pleroma application
**Only works when configuration from database is enabled.**
- Params: none - Params: none
- Response: - Response:
- On failure: - On failure:
@ -782,11 +797,24 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
{} {}
``` ```
## `GET /api/pleroma/admin/need_reboot`
### Returns the flag whether the pleroma should be restarted
- Params: none
- Response:
- `need_reboot` - boolean
```json
{
"need_reboot": false
}
```
## `GET /api/pleroma/admin/config` ## `GET /api/pleroma/admin/config`
### Get list of merged default settings with saved in database. ### Get list of merged default settings with saved in database.
*If `need_reboot` flag exists in response, instance must be restarted, so reboot time settings can take effect.* *If `need_reboot` is `true`, instance must be restarted, so reboot time settings can take effect.*
**Only works when configuration from database is enabled.** **Only works when configuration from database is enabled.**
@ -808,13 +836,12 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
"need_reboot": true "need_reboot": true
} }
``` ```
need_reboot - *optional*, if were changed reboot time settings.
## `POST /api/pleroma/admin/config` ## `POST /api/pleroma/admin/config`
### Update config settings ### Update config settings
*If `need_reboot` flag exists in response, instance must be restarted, so reboot time settings can take effect.* *If `need_reboot` is `true`, instance must be restarted, so reboot time settings can take effect.*
**Only works when configuration from database is enabled.** **Only works when configuration from database is enabled.**
@ -958,7 +985,6 @@ config :quack,
"need_reboot": true "need_reboot": true
} }
``` ```
need_reboot - *optional*, if were changed reboot time settings.
## ` GET /api/pleroma/admin/config/descriptions` ## ` GET /api/pleroma/admin/config/descriptions`
@ -1062,3 +1088,104 @@ Loads json generated from `config/descriptions.exs`.
} }
} }
``` ```
## `GET /api/pleroma/admin/oauth_app`
### List OAuth app
- Params:
- *optional* `name`
- *optional* `client_id`
- *optional* `page`
- *optional* `page_size`
- *optional* `trusted`
- Response:
```json
{
"apps": [
{
"id": 1,
"name": "App name",
"client_id": "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
"client_secret": "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
"redirect_uri": "https://example.com/oauth-callback",
"website": "https://example.com",
"trusted": true
}
],
"count": 17,
"page_size": 50
}
```
## `POST /api/pleroma/admin/oauth_app`
### Create OAuth App
- Params:
- `name`
- `redirect_uris`
- `scopes`
- *optional* `website`
- *optional* `trusted`
- Response:
```json
{
"id": 1,
"name": "App name",
"client_id": "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
"client_secret": "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
"redirect_uri": "https://example.com/oauth-callback",
"website": "https://example.com",
"trusted": true
}
```
- On failure:
```json
{
"redirect_uris": "can't be blank",
"name": "can't be blank"
}
```
## `PATCH /api/pleroma/admin/oauth_app/:id`
### Update OAuth App
- Params:
- *optional* `name`
- *optional* `redirect_uris`
- *optional* `scopes`
- *optional* `website`
- *optional* `trusted`
- Response:
```json
{
"id": 1,
"name": "App name",
"client_id": "yHoDSiWYp5mPV6AfsaVOWjdOyt5PhWRiafi6MRd1lSk",
"client_secret": "nLmis486Vqrv2o65eM9mLQx_m_4gH-Q6PcDpGIMl6FY",
"redirect_uri": "https://example.com/oauth-callback",
"website": "https://example.com",
"trusted": true
}
```
## `DELETE /api/pleroma/admin/oauth_app/:id`
### Delete OAuth App
- Params: None
- Response:
- On success: `204`, empty response
- On failure:
- 400 Bad Request `"Invalid parameters"` when `status` is missing

View file

@ -0,0 +1,16 @@
# Creating trusted OAuth App
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
## Create trusted OAuth App.
Optional params:
* `-s SCOPES` - scopes for app, e.g. `read,write,follow,push`.
```sh tab="OTP"
./bin/pleroma_ctl app create -n APP_NAME -r REDIRECT_URI
```
```sh tab="From Source"
mix pleroma.app create -n APP_NAME -r REDIRECT_URI
```

View file

@ -113,7 +113,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RewritePolicy do
@impl true @impl true
def describe do def describe do
{:ok, %{mrf_sample: %{content: "new message content"}}}` {:ok, %{mrf_sample: %{content: "new message content"}}}
end end
end end
``` ```

View file

@ -5,7 +5,6 @@
defmodule Mix.Pleroma do defmodule Mix.Pleroma do
@doc "Common functions to be reused in mix tasks" @doc "Common functions to be reused in mix tasks"
def start_pleroma do def start_pleroma do
Mix.Task.run("app.start")
Application.put_env(:phoenix, :serve_endpoints, false, persistent: true) Application.put_env(:phoenix, :serve_endpoints, false, persistent: true)
if Pleroma.Config.get(:env) != :test do if Pleroma.Config.get(:env) != :test do

View file

@ -0,0 +1,49 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.App do
@moduledoc File.read!("docs/administration/CLI_tasks/oauth_app.md")
use Mix.Task
import Mix.Pleroma
@shortdoc "Creates trusted OAuth App"
def run(["create" | options]) do
start_pleroma()
{opts, _} =
OptionParser.parse!(options,
strict: [name: :string, redirect_uri: :string, scopes: :string],
aliases: [n: :name, r: :redirect_uri, s: :scopes]
)
scopes =
if opts[:scopes] do
String.split(opts[:scopes], ",")
else
["read", "write", "follow", "push"]
end
params = %{
client_name: opts[:name],
redirect_uris: opts[:redirect_uri],
trusted: true,
scopes: scopes
}
with {:ok, app} <- Pleroma.Web.OAuth.App.create(params) do
shell_info("#{app.client_name} successfully created:")
shell_info("App client_id: " <> app.client_id)
shell_info("App client_secret: " <> app.client_secret)
else
{:error, changeset} ->
shell_error("Creating failed:")
Enum.each(Pleroma.Web.OAuth.App.errors(changeset), fn {key, error} ->
shell_error("#{key}: #{error}")
end)
end
end
end

View file

@ -67,7 +67,8 @@ defmodule Mix.Tasks.Pleroma.Benchmark do
Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
activities: activities, activities: activities,
for: user, for: user,
as: :activity as: :activity,
skip_relationships: true
}) })
end end
}, },

View file

@ -54,10 +54,19 @@ defmodule Pleroma.Config.TransferTask do
[:pleroma, nil, :prometheus] [:pleroma, nil, :prometheus]
end end
{logger, other} =
(Repo.all(ConfigDB) ++ deleted_settings)
|> Enum.map(&transform_and_merge/1)
|> Enum.split_with(fn {group, _, _, _} -> group in [:logger, :quack] end)
logger
|> Enum.sort()
|> Enum.each(&configure/1)
started_applications = Application.started_applications() started_applications = Application.started_applications()
(Repo.all(ConfigDB) ++ deleted_settings) other
|> Enum.map(&merge_and_update/1) |> Enum.map(&update/1)
|> Enum.uniq() |> Enum.uniq()
|> Enum.reject(&(&1 in reject_restart)) |> Enum.reject(&(&1 in reject_restart))
|> maybe_set_pleroma_last() |> maybe_set_pleroma_last()
@ -81,51 +90,71 @@ defmodule Pleroma.Config.TransferTask do
end end
end end
defp group_for_restart(:logger, key, _, merged_value) do defp transform_and_merge(%{group: group, key: key, value: value} = setting) do
# change logger configuration in runtime, without restart group = ConfigDB.from_string(group)
if Keyword.keyword?(merged_value) and key = ConfigDB.from_string(key)
key not in [:compile_time_application, :backends, :compile_time_purge_matching] do value = ConfigDB.from_binary(value)
Logger.configure_backend(key, merged_value)
else
Logger.configure([{key, merged_value}])
end
nil default = Config.Holder.default_config(group, key)
merged =
cond do
Ecto.get_meta(setting, :state) == :deleted -> default
can_be_merged?(default, value) -> ConfigDB.merge_group(group, key, default, value)
true -> value
end
{group, key, value, merged}
end end
defp group_for_restart(group, _, _, _) when group != :pleroma, do: group # change logger configuration in runtime, without restart
defp configure({:quack, key, _, merged}) do
defp group_for_restart(group, key, value, _) do Logger.configure_backend(Quack.Logger, [{key, merged}])
if pleroma_need_restart?(group, key, value), do: group :ok = update_env(:quack, key, merged)
end end
defp merge_and_update(setting) do defp configure({_, :backends, _, merged}) do
# removing current backends
Enum.each(Application.get_env(:logger, :backends), &Logger.remove_backend/1)
Enum.each(merged, &Logger.add_backend/1)
:ok = update_env(:logger, :backends, merged)
end
defp configure({_, key, _, merged}) when key in [:console, :ex_syslogger] do
merged =
if key == :console do
put_in(merged[:format], merged[:format] <> "\n")
else
merged
end
backend =
if key == :ex_syslogger,
do: {ExSyslogger, :ex_syslogger},
else: key
Logger.configure_backend(backend, merged)
:ok = update_env(:logger, key, merged)
end
defp configure({_, key, _, merged}) do
Logger.configure([{key, merged}])
:ok = update_env(:logger, key, merged)
end
defp update({group, key, value, merged}) do
try do try do
key = ConfigDB.from_string(setting.key) :ok = update_env(group, key, merged)
group = ConfigDB.from_string(setting.group)
default = Config.Holder.default_config(group, key) if group != :pleroma or pleroma_need_restart?(group, key, value), do: group
value = ConfigDB.from_binary(setting.value)
merged_value =
cond do
Ecto.get_meta(setting, :state) == :deleted -> default
can_be_merged?(default, value) -> ConfigDB.merge_group(group, key, default, value)
true -> value
end
:ok = update_env(group, key, merged_value)
group_for_restart(group, key, value, merged_value)
rescue rescue
error -> error ->
error_msg = error_msg =
"updating env causes error, group: " <> "updating env causes error, group: #{inspect(group)}, key: #{inspect(key)}, value: #{
inspect(setting.group) <> inspect(value)
" key: " <> } error: #{inspect(error)}"
inspect(setting.key) <>
" value: " <>
inspect(ConfigDB.from_binary(setting.value)) <> " error: " <> inspect(error)
Logger.warn(error_msg) Logger.warn(error_msg)
@ -133,6 +162,9 @@ defmodule Pleroma.Config.TransferTask do
end end
end end
defp update_env(group, key, nil), do: Application.delete_env(group, key)
defp update_env(group, key, value), do: Application.put_env(group, key, value)
@spec pleroma_need_restart?(atom(), atom(), any()) :: boolean() @spec pleroma_need_restart?(atom(), atom(), any()) :: boolean()
def pleroma_need_restart?(group, key, value) do def pleroma_need_restart?(group, key, value) do
group_and_key_need_reboot?(group, key) or group_and_subkey_need_reboot?(group, key, value) group_and_key_need_reboot?(group, key) or group_and_subkey_need_reboot?(group, key, value)
@ -150,9 +182,6 @@ defmodule Pleroma.Config.TransferTask do
end) end)
end end
defp update_env(group, key, nil), do: Application.delete_env(group, key)
defp update_env(group, key, value), do: Application.put_env(group, key, value)
defp restart(_, :pleroma, env), do: Restarter.Pleroma.restart_after_boot(env) defp restart(_, :pleroma, env), do: Restarter.Pleroma.restart_after_boot(env)
defp restart(started_applications, app, _) do defp restart(started_applications, app, _) do

View file

@ -4,10 +4,16 @@
import EctoEnum import EctoEnum
defenum(UserRelationshipTypeEnum, defenum(Pleroma.UserRelationship.Type,
block: 1, block: 1,
mute: 2, mute: 2,
reblog_mute: 3, reblog_mute: 3,
notification_mute: 4, notification_mute: 4,
inverse_subscription: 5 inverse_subscription: 5
) )
defenum(Pleroma.FollowingRelationship.State,
follow_pending: 1,
follow_accept: 2,
follow_reject: 3
)

View file

@ -38,22 +38,14 @@ defmodule Pleroma.Emoji.Formatter do
def demojify(text, nil), do: text def demojify(text, nil), do: text
@doc "Outputs a list of the emoji-shortcodes in a text"
def get_emoji(text) when is_binary(text) do
Enum.filter(Emoji.get_all(), fn {emoji, %Emoji{}} ->
String.contains?(text, ":#{emoji}:")
end)
end
def get_emoji(_), do: []
@doc "Outputs a list of the emoji-Maps in a text" @doc "Outputs a list of the emoji-Maps in a text"
def get_emoji_map(text) when is_binary(text) do def get_emoji_map(text) when is_binary(text) do
get_emoji(text) Emoji.get_all()
|> Enum.filter(fn {emoji, %Emoji{}} -> String.contains?(text, ":#{emoji}:") end)
|> Enum.reduce(%{}, fn {name, %Emoji{file: file}}, acc -> |> Enum.reduce(%{}, fn {name, %Emoji{file: file}}, acc ->
Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}") Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}")
end) end)
end end
def get_emoji_map(_), do: [] def get_emoji_map(_), do: %{}
end end

View file

@ -8,12 +8,13 @@ defmodule Pleroma.FollowingRelationship do
import Ecto.Changeset import Ecto.Changeset
import Ecto.Query import Ecto.Query
alias Ecto.Changeset
alias FlakeId.Ecto.CompatType alias FlakeId.Ecto.CompatType
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
schema "following_relationships" do schema "following_relationships" do
field(:state, :string, default: "accept") field(:state, Pleroma.FollowingRelationship.State, default: :follow_pending)
belongs_to(:follower, User, type: CompatType) belongs_to(:follower, User, type: CompatType)
belongs_to(:following, User, type: CompatType) belongs_to(:following, User, type: CompatType)
@ -27,6 +28,18 @@ defmodule Pleroma.FollowingRelationship do
|> put_assoc(:follower, attrs.follower) |> put_assoc(:follower, attrs.follower)
|> put_assoc(:following, attrs.following) |> put_assoc(:following, attrs.following)
|> validate_required([:state, :follower, :following]) |> validate_required([:state, :follower, :following])
|> unique_constraint(:follower_id,
name: :following_relationships_follower_id_following_id_index
)
|> validate_not_self_relationship()
end
def state_to_enum(state) when state in ["pending", "accept", "reject"] do
String.to_existing_atom("follow_#{state}")
end
def state_to_enum(state) do
raise "State is not convertible to Pleroma.FollowingRelationship.State: #{state}"
end end
def get(%User{} = follower, %User{} = following) do def get(%User{} = follower, %User{} = following) do
@ -35,7 +48,7 @@ defmodule Pleroma.FollowingRelationship do
|> Repo.one() |> Repo.one()
end end
def update(follower, following, "reject"), do: unfollow(follower, following) def update(follower, following, :follow_reject), do: unfollow(follower, following)
def update(%User{} = follower, %User{} = following, state) do def update(%User{} = follower, %User{} = following, state) do
case get(follower, following) do case get(follower, following) do
@ -50,7 +63,7 @@ defmodule Pleroma.FollowingRelationship do
end end
end end
def follow(%User{} = follower, %User{} = following, state \\ "accept") do def follow(%User{} = follower, %User{} = following, state \\ :follow_accept) do
%__MODULE__{} %__MODULE__{}
|> changeset(%{follower: follower, following: following, state: state}) |> changeset(%{follower: follower, following: following, state: state})
|> Repo.insert(on_conflict: :nothing) |> Repo.insert(on_conflict: :nothing)
@ -80,7 +93,7 @@ defmodule Pleroma.FollowingRelationship do
def get_follow_requests(%User{id: id}) do def get_follow_requests(%User{id: id}) do
__MODULE__ __MODULE__
|> join(:inner, [r], f in assoc(r, :follower)) |> join(:inner, [r], f in assoc(r, :follower))
|> where([r], r.state == "pending") |> where([r], r.state == ^:follow_pending)
|> where([r], r.following_id == ^id) |> where([r], r.following_id == ^id)
|> select([r, f], f) |> select([r, f], f)
|> Repo.all() |> Repo.all()
@ -88,7 +101,7 @@ defmodule Pleroma.FollowingRelationship do
def following?(%User{id: follower_id}, %User{id: followed_id}) do def following?(%User{id: follower_id}, %User{id: followed_id}) do
__MODULE__ __MODULE__
|> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept") |> where(follower_id: ^follower_id, following_id: ^followed_id, state: ^:follow_accept)
|> Repo.exists?() |> Repo.exists?()
end end
@ -97,7 +110,7 @@ defmodule Pleroma.FollowingRelationship do
__MODULE__ __MODULE__
|> join(:inner, [r], u in User, on: r.following_id == u.id) |> join(:inner, [r], u in User, on: r.following_id == u.id)
|> where([r], r.follower_id == ^user.id) |> where([r], r.follower_id == ^user.id)
|> where([r], r.state == "accept") |> where([r], r.state == ^:follow_accept)
|> select([r, u], u.follower_address) |> select([r, u], u.follower_address)
|> Repo.all() |> Repo.all()
@ -157,4 +170,30 @@ defmodule Pleroma.FollowingRelationship do
fr -> fr.follower_id == follower.id and fr.following_id == following.id fr -> fr.follower_id == follower.id and fr.following_id == following.id
end) end)
end end
defp validate_not_self_relationship(%Changeset{} = changeset) do
changeset
|> validate_follower_id_following_id_inequality()
|> validate_following_id_follower_id_inequality()
end
defp validate_follower_id_following_id_inequality(%Changeset{} = changeset) do
validate_change(changeset, :follower_id, fn _, follower_id ->
if follower_id == get_field(changeset, :following_id) do
[source_id: "can't be equal to following_id"]
else
[]
end
end)
end
defp validate_following_id_follower_id_inequality(%Changeset{} = changeset) do
validate_change(changeset, :following_id, fn _, following_id ->
if following_id == get_field(changeset, :follower_id) do
[target_id: "can't be equal to follower_id"]
else
[]
end
end)
end
end end

View file

@ -31,13 +31,23 @@ defmodule Pleroma.Formatter do
def mention_handler("@" <> nickname, buffer, opts, acc) do def mention_handler("@" <> nickname, buffer, opts, acc) do
case User.get_cached_by_nickname(nickname) do case User.get_cached_by_nickname(nickname) do
%User{id: id} = user -> %User{id: id} = user ->
ap_id = get_ap_id(user) user_url = user.uri || user.ap_id
nickname_text = get_nickname_text(nickname, opts) nickname_text = get_nickname_text(nickname, opts)
link = link =
~s(<span class="h-card"><a data-user="#{id}" class="u-url mention" href="#{ap_id}" rel="ugc">@<span>#{ Phoenix.HTML.Tag.content_tag(
nickname_text :span,
}</span></a></span>) Phoenix.HTML.Tag.content_tag(
:a,
["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)],
"data-user": id,
class: "u-url mention",
href: user_url,
rel: "ugc"
),
class: "h-card"
)
|> Phoenix.HTML.safe_to_string()
{link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}} {link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
@ -49,7 +59,15 @@ defmodule Pleroma.Formatter do
def hashtag_handler("#" <> tag = tag_text, _buffer, _opts, acc) do def hashtag_handler("#" <> tag = tag_text, _buffer, _opts, acc) do
tag = String.downcase(tag) tag = String.downcase(tag)
url = "#{Pleroma.Web.base_url()}/tag/#{tag}" url = "#{Pleroma.Web.base_url()}/tag/#{tag}"
link = ~s(<a class="hashtag" data-tag="#{tag}" href="#{url}" rel="tag ugc">#{tag_text}</a>)
link =
Phoenix.HTML.Tag.content_tag(:a, tag_text,
class: "hashtag",
"data-tag": tag,
href: url,
rel: "tag ugc"
)
|> Phoenix.HTML.safe_to_string()
{link, %{acc | tags: MapSet.put(acc.tags, {tag_text, tag})}} {link, %{acc | tags: MapSet.put(acc.tags, {tag_text, tag})}}
end end
@ -128,9 +146,6 @@ defmodule Pleroma.Formatter do
end end
end end
defp get_ap_id(%User{source_data: %{"url" => url}}) when is_binary(url), do: url
defp get_ap_id(%User{ap_id: ap_id}), do: ap_id
defp get_nickname_text(nickname, %{mentions_format: :full}), do: User.full_nickname(nickname) defp get_nickname_text(nickname, %{mentions_format: :full}), do: User.full_nickname(nickname)
defp get_nickname_text(nickname, _), do: User.local_nickname(nickname) defp get_nickname_text(nickname, _), do: User.local_nickname(nickname)
end end

View file

@ -49,8 +49,10 @@ defmodule Pleroma.Gun.Conn do
key = "#{uri.scheme}:#{uri.host}:#{uri.port}" key = "#{uri.scheme}:#{uri.host}:#{uri.port}"
max_connections = pool_opts[:max_connections] || 250
conn_pid = conn_pid =
if Connections.count(name) < opts[:max_connection] do if Connections.count(name) < max_connections do
do_open(uri, opts) do_open(uri, opts)
else else
close_least_used_and_do_open(name, uri, opts) close_least_used_and_do_open(name, uri, opts)

View file

@ -0,0 +1,17 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.AuthExpectedPlug do
import Plug.Conn
def init(options), do: options
def call(conn, _) do
put_private(conn, :auth_expected, true)
end
def auth_expected?(conn) do
conn.private[:auth_expected]
end
end

View file

@ -42,13 +42,13 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
else else
{:user_match, false} -> {:user_match, false} ->
Logger.debug("Failed to map identity from signature (payload actor mismatch)") Logger.debug("Failed to map identity from signature (payload actor mismatch)")
Logger.debug("key_id=#{key_id_from_conn(conn)}, actor=#{actor}") Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{inspect(actor)}")
assign(conn, :valid_signature, false) assign(conn, :valid_signature, false)
# remove me once testsuite uses mapped capabilities instead of what we do now # remove me once testsuite uses mapped capabilities instead of what we do now
{:user, nil} -> {:user, nil} ->
Logger.debug("Failed to map identity from signature (lookup failure)") Logger.debug("Failed to map identity from signature (lookup failure)")
Logger.debug("key_id=#{key_id_from_conn(conn)}, actor=#{actor}") Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}")
conn conn
end end
end end
@ -60,7 +60,7 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
else else
_ -> _ ->
Logger.debug("Failed to map identity from signature (no payload actor mismatch)") Logger.debug("Failed to map identity from signature (no payload actor mismatch)")
Logger.debug("key_id=#{key_id_from_conn(conn)}") Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}")
assign(conn, :valid_signature, false) assign(conn, :valid_signature, false)
end end
end end

View file

@ -8,12 +8,15 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
alias Pleroma.Plugs.PlugHelper
use Pleroma.Web, :plug
@behaviour Plug @behaviour Plug
def init(%{scopes: _} = options), do: options def init(%{scopes: _} = options), do: options
def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
op = options[:op] || :| op = options[:op] || :|
token = assigns[:token] token = assigns[:token]

View file

@ -0,0 +1,38 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.PlugHelper do
@moduledoc "Pleroma Plug helper"
def append_to_called_plugs(conn, plug_module) do
append_to_private_list(conn, :called_plugs, plug_module)
end
def append_to_skipped_plugs(conn, plug_module) do
append_to_private_list(conn, :skipped_plugs, plug_module)
end
def plug_called?(conn, plug_module) do
contained_in_private_list?(conn, :called_plugs, plug_module)
end
def plug_skipped?(conn, plug_module) do
contained_in_private_list?(conn, :skipped_plugs, plug_module)
end
def plug_called_or_skipped?(conn, plug_module) do
plug_called?(conn, plug_module) || plug_skipped?(conn, plug_module)
end
defp append_to_private_list(conn, private_variable, value) do
list = conn.private[private_variable] || []
modified_list = Enum.uniq(list ++ [value])
Plug.Conn.put_private(conn, private_variable, modified_list)
end
defp contained_in_private_list?(conn, private_variable, value) do
list = conn.private[private_variable] || []
value in list
end
end

View file

@ -110,20 +110,9 @@ defmodule Pleroma.Plugs.RateLimiter do
end end
def disabled?(conn) do def disabled?(conn) do
localhost_or_socket = if Map.has_key?(conn.assigns, :remote_ip_found),
case Config.get([Pleroma.Web.Endpoint, :http, :ip]) do do: !conn.assigns.remote_ip_found,
{127, 0, 0, 1} -> true else: false
{0, 0, 0, 0, 0, 0, 0, 1} -> true
{:local, _} -> true
_ -> false
end
remote_ip_not_found =
if Map.has_key?(conn.assigns, :remote_ip_found),
do: !conn.assigns.remote_ip_found,
else: false
localhost_or_socket and remote_ip_not_found
end end
@inspect_bucket_not_found {:error, :not_found} @inspect_bucket_not_found {:error, :not_found}

View file

@ -7,8 +7,6 @@ defmodule Pleroma.Plugs.RemoteIp do
This is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. This is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
""" """
import Plug.Conn
@behaviour Plug @behaviour Plug
@headers ~w[ @headers ~w[
@ -28,12 +26,11 @@ defmodule Pleroma.Plugs.RemoteIp do
def init(_), do: nil def init(_), do: nil
def call(%{remote_ip: original_remote_ip} = conn, _) do def call(conn, _) do
config = Pleroma.Config.get(__MODULE__, []) config = Pleroma.Config.get(__MODULE__, [])
if Keyword.get(config, :enabled, false) do if Keyword.get(config, :enabled, false) do
%{remote_ip: new_remote_ip} = conn = RemoteIp.call(conn, remote_ip_opts(config)) RemoteIp.call(conn, remote_ip_opts(config))
assign(conn, :remote_ip_found, original_remote_ip != new_remote_ip)
else else
conn conn
end end

View file

@ -41,6 +41,7 @@ defmodule Pleroma.Plugs.UploadedMedia do
conn -> conn ->
conn conn
end end
|> merge_resp_headers([{"content-security-policy", "sandbox"}])
config = Pleroma.Config.get(Pleroma.Upload) config = Pleroma.Config.get(Pleroma.Upload)

View file

@ -243,7 +243,7 @@ defmodule Pleroma.Pool.Connections do
@impl true @impl true
def handle_info({:DOWN, _ref, :process, conn_pid, reason}, state) do def handle_info({:DOWN, _ref, :process, conn_pid, reason}, state) do
Logger.debug("received DOWM message for #{inspect(conn_pid)} reason -> #{inspect(reason)}") Logger.debug("received DOWN message for #{inspect(conn_pid)} reason -> #{inspect(reason)}")
state = state =
with {key, conn} <- find_conn(state.conns, conn_pid) do with {key, conn} <- find_conn(state.conns, conn_pid) do

View file

@ -0,0 +1,31 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
# A test controller reachable only in :test env.
# Serves to test OAuth scopes check skipping / enforcement.
defmodule Pleroma.Tests.OAuthTestController do
@moduledoc false
use Pleroma.Web, :controller
alias Pleroma.Plugs.OAuthScopesPlug
plug(:skip_plug, OAuthScopesPlug when action == :skipped_oauth)
plug(OAuthScopesPlug, %{scopes: ["read"]} when action != :missed_oauth)
def skipped_oauth(conn, _params) do
noop(conn)
end
def performed_oauth(conn, _params) do
noop(conn)
end
def missed_oauth(conn, _params) do
noop(conn)
end
defp noop(conn), do: json(conn, %{})
end

View file

@ -15,6 +15,7 @@ defmodule Pleroma.User do
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Conversation.Participation alias Pleroma.Conversation.Participation
alias Pleroma.Delivery alias Pleroma.Delivery
alias Pleroma.Emoji
alias Pleroma.FollowingRelationship alias Pleroma.FollowingRelationship
alias Pleroma.Formatter alias Pleroma.Formatter
alias Pleroma.HTML alias Pleroma.HTML
@ -28,6 +29,7 @@ defmodule Pleroma.User do
alias Pleroma.UserRelationship alias Pleroma.UserRelationship
alias Pleroma.Web alias Pleroma.Web
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
@ -82,6 +84,7 @@ defmodule Pleroma.User do
field(:password, :string, virtual: true) field(:password, :string, virtual: true)
field(:password_confirmation, :string, virtual: true) field(:password_confirmation, :string, virtual: true)
field(:keys, :string) field(:keys, :string)
field(:public_key, :string)
field(:ap_id, :string) field(:ap_id, :string)
field(:avatar, :map) field(:avatar, :map)
field(:local, :boolean, default: true) field(:local, :boolean, default: true)
@ -94,7 +97,6 @@ defmodule Pleroma.User do
field(:last_digest_emailed_at, :naive_datetime) field(:last_digest_emailed_at, :naive_datetime)
field(:banner, :map, default: %{}) field(:banner, :map, default: %{})
field(:background, :map, default: %{}) field(:background, :map, default: %{})
field(:source_data, :map, default: %{})
field(:note_count, :integer, default: 0) field(:note_count, :integer, default: 0)
field(:follower_count, :integer, default: 0) field(:follower_count, :integer, default: 0)
field(:following_count, :integer, default: 0) field(:following_count, :integer, default: 0)
@ -112,7 +114,7 @@ defmodule Pleroma.User do
field(:show_role, :boolean, default: true) field(:show_role, :boolean, default: true)
field(:settings, :map, default: nil) field(:settings, :map, default: nil)
field(:magic_key, :string, default: nil) field(:magic_key, :string, default: nil)
field(:uri, :string, default: nil) field(:uri, Types.Uri, default: nil)
field(:hide_followers_count, :boolean, default: false) field(:hide_followers_count, :boolean, default: false)
field(:hide_follows_count, :boolean, default: false) field(:hide_follows_count, :boolean, default: false)
field(:hide_followers, :boolean, default: false) field(:hide_followers, :boolean, default: false)
@ -122,7 +124,7 @@ defmodule Pleroma.User do
field(:pinned_activities, {:array, :string}, default: []) field(:pinned_activities, {:array, :string}, default: [])
field(:email_notifications, :map, default: %{"digest" => false}) field(:email_notifications, :map, default: %{"digest" => false})
field(:mascot, :map, default: nil) field(:mascot, :map, default: nil)
field(:emoji, {:array, :map}, default: []) field(:emoji, :map, default: %{})
field(:pleroma_settings_store, :map, default: %{}) field(:pleroma_settings_store, :map, default: %{})
field(:fields, {:array, :map}, default: []) field(:fields, {:array, :map}, default: [])
field(:raw_fields, {:array, :map}, default: []) field(:raw_fields, {:array, :map}, default: [])
@ -132,6 +134,8 @@ defmodule Pleroma.User do
field(:skip_thread_containment, :boolean, default: false) field(:skip_thread_containment, :boolean, default: false)
field(:actor_type, :string, default: "Person") field(:actor_type, :string, default: "Person")
field(:also_known_as, {:array, :string}, default: []) field(:also_known_as, {:array, :string}, default: [])
field(:inbox, :string)
field(:shared_inbox, :string)
embeds_one( embeds_one(
:notification_settings, :notification_settings,
@ -306,6 +310,7 @@ defmodule Pleroma.User do
end end
end end
# Should probably be renamed or removed
def ap_id(%User{nickname: nickname}), do: "#{Web.base_url()}/users/#{nickname}" def ap_id(%User{nickname: nickname}), do: "#{Web.base_url()}/users/#{nickname}"
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
@ -339,62 +344,72 @@ defmodule Pleroma.User do
end end
end end
def remote_user_creation(params) do defp fix_follower_address(%{follower_address: _, following_address: _} = params), do: params
defp fix_follower_address(%{nickname: nickname} = params),
do: Map.put(params, :follower_address, ap_followers(%User{nickname: nickname}))
defp fix_follower_address(params), do: params
def remote_user_changeset(struct \\ %User{local: false}, params) do
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000) bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
name =
case params[:name] do
name when is_binary(name) and byte_size(name) > 0 -> name
_ -> params[:nickname]
end
params = params =
params params
|> Map.put(:name, name)
|> Map.put_new(:last_refreshed_at, NaiveDateTime.utc_now())
|> truncate_if_exists(:name, name_limit) |> truncate_if_exists(:name, name_limit)
|> truncate_if_exists(:bio, bio_limit) |> truncate_if_exists(:bio, bio_limit)
|> truncate_fields_param() |> truncate_fields_param()
|> fix_follower_address()
changeset = struct
%User{local: false} |> cast(
|> cast( params,
params, [
[ :bio,
:bio, :name,
:name, :emoji,
:ap_id, :ap_id,
:nickname, :inbox,
:avatar, :shared_inbox,
:ap_enabled, :nickname,
:source_data, :public_key,
:banner, :avatar,
:locked, :ap_enabled,
:magic_key, :banner,
:uri, :locked,
:hide_followers, :last_refreshed_at,
:hide_follows, :magic_key,
:hide_followers_count, :uri,
:hide_follows_count, :follower_address,
:follower_count, :following_address,
:fields, :hide_followers,
:following_count, :hide_follows,
:discoverable, :hide_followers_count,
:invisible, :hide_follows_count,
:actor_type, :follower_count,
:also_known_as :fields,
] :following_count,
) :discoverable,
|> validate_required([:name, :ap_id]) :invisible,
|> unique_constraint(:nickname) :actor_type,
|> validate_format(:nickname, @email_regex) :also_known_as
|> validate_length(:bio, max: bio_limit) ]
|> validate_length(:name, max: name_limit) )
|> validate_fields(true) |> validate_required([:name, :ap_id])
|> unique_constraint(:nickname)
case params[:source_data] do |> validate_format(:nickname, @email_regex)
%{"followers" => followers, "following" => following} -> |> validate_length(:bio, max: bio_limit)
changeset |> validate_length(:name, max: name_limit)
|> put_change(:follower_address, followers) |> validate_fields(true)
|> put_change(:following_address, following)
_ ->
followers = ap_followers(%User{nickname: get_field(changeset, :nickname)})
put_change(changeset, :follower_address, followers)
end
end end
def update_changeset(struct, params \\ %{}) do def update_changeset(struct, params \\ %{}) do
@ -407,7 +422,11 @@ defmodule Pleroma.User do
[ [
:bio, :bio,
:name, :name,
:emoji,
:avatar, :avatar,
:public_key,
:inbox,
:shared_inbox,
:locked, :locked,
:no_rich_text, :no_rich_text,
:default_scope, :default_scope,
@ -434,6 +453,7 @@ defmodule Pleroma.User do
|> validate_length(:bio, max: bio_limit) |> validate_length(:bio, max: bio_limit)
|> validate_length(:name, min: 1, max: name_limit) |> validate_length(:name, min: 1, max: name_limit)
|> put_fields() |> put_fields()
|> put_emoji()
|> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)}) |> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
|> put_change_if_present(:avatar, &put_upload(&1, :avatar)) |> put_change_if_present(:avatar, &put_upload(&1, :avatar))
|> put_change_if_present(:banner, &put_upload(&1, :banner)) |> put_change_if_present(:banner, &put_upload(&1, :banner))
@ -469,6 +489,18 @@ defmodule Pleroma.User do
|> elem(0) |> elem(0)
end end
defp put_emoji(changeset) do
bio = get_change(changeset, :bio)
name = get_change(changeset, :name)
if bio || name do
emoji = Map.merge(Emoji.Formatter.get_emoji_map(bio), Emoji.Formatter.get_emoji_map(name))
put_change(changeset, :emoji, emoji)
else
changeset
end
end
defp put_change_if_present(changeset, map_field, value_function) do defp put_change_if_present(changeset, map_field, value_function) do
if value = get_change(changeset, map_field) do if value = get_change(changeset, map_field) do
with {:ok, new_value} <- value_function.(value) do with {:ok, new_value} <- value_function.(value) do
@ -488,49 +520,6 @@ defmodule Pleroma.User do
end end
end end
def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
params = Map.put(params, :last_refreshed_at, NaiveDateTime.utc_now())
params = if remote?, do: truncate_fields_param(params), else: params
struct
|> cast(
params,
[
:bio,
:name,
:follower_address,
:following_address,
:avatar,
:last_refreshed_at,
:ap_enabled,
:source_data,
:banner,
:locked,
:magic_key,
:follower_count,
:following_count,
:hide_follows,
:fields,
:hide_followers,
:allow_following_move,
:discoverable,
:hide_followers_count,
:hide_follows_count,
:actor_type,
:also_known_as
]
)
|> unique_constraint(:nickname)
|> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: bio_limit)
|> validate_length(:name, max: name_limit)
|> validate_fields(remote?)
end
def update_as_admin_changeset(struct, params) do def update_as_admin_changeset(struct, params) do
struct struct
|> update_changeset(params) |> update_changeset(params)
@ -606,7 +595,7 @@ defmodule Pleroma.User do
struct struct
|> confirmation_changeset(need_confirmation: need_confirmation?) |> confirmation_changeset(need_confirmation: need_confirmation?)
|> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation]) |> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation, :emoji])
|> validate_required([:name, :nickname, :password, :password_confirmation]) |> validate_required([:name, :nickname, :password, :password_confirmation])
|> validate_confirmation(:password) |> validate_confirmation(:password)
|> unique_constraint(:email) |> unique_constraint(:email)
@ -702,7 +691,7 @@ defmodule Pleroma.User do
# "Locked" (self-locked) users demand explicit authorization of follow requests # "Locked" (self-locked) users demand explicit authorization of follow requests
def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do
follow(follower, followed, "pending") follow(follower, followed, :follow_pending)
end end
def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do
@ -722,14 +711,14 @@ defmodule Pleroma.User do
def follow_all(follower, followeds) do def follow_all(follower, followeds) do
followeds followeds
|> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end) |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end)
|> Enum.each(&follow(follower, &1, "accept")) |> Enum.each(&follow(follower, &1, :follow_accept))
set_cache(follower) set_cache(follower)
end end
defdelegate following(user), to: FollowingRelationship defdelegate following(user), to: FollowingRelationship
def follow(%User{} = follower, %User{} = followed, state \\ "accept") do def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do
deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked]) deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
cond do cond do
@ -756,7 +745,7 @@ defmodule Pleroma.User do
def unfollow(%User{} = follower, %User{} = followed) do def unfollow(%User{} = follower, %User{} = followed) do
case get_follow_state(follower, followed) do case get_follow_state(follower, followed) do
state when state in ["accept", "pending"] -> state when state in [:follow_pending, :follow_accept] ->
FollowingRelationship.unfollow(follower, followed) FollowingRelationship.unfollow(follower, followed)
{:ok, followed} = update_follower_count(followed) {:ok, followed} = update_follower_count(followed)
@ -774,6 +763,7 @@ defmodule Pleroma.User do
defdelegate following?(follower, followed), to: FollowingRelationship defdelegate following?(follower, followed), to: FollowingRelationship
@doc "Returns follow state as Pleroma.FollowingRelationship.State value"
def get_follow_state(%User{} = follower, %User{} = following) do def get_follow_state(%User{} = follower, %User{} = following) do
following_relationship = FollowingRelationship.get(follower, following) following_relationship = FollowingRelationship.get(follower, following)
get_follow_state(follower, following, following_relationship) get_follow_state(follower, following, following_relationship)
@ -787,8 +777,11 @@ defmodule Pleroma.User do
case {following_relationship, following.local} do case {following_relationship, following.local} do
{nil, false} -> {nil, false} ->
case Utils.fetch_latest_follow(follower, following) do case Utils.fetch_latest_follow(follower, following) do
%{data: %{"state" => state}} when state in ["pending", "accept"] -> state %Activity{data: %{"state" => state}} when state in ["pending", "accept"] ->
_ -> nil FollowingRelationship.state_to_enum(state)
_ ->
nil
end end
{%{state: state}, _} -> {%{state: state}, _} ->
@ -1287,7 +1280,7 @@ defmodule Pleroma.User do
def blocks?(%User{} = user, %User{} = target) do def blocks?(%User{} = user, %User{} = target) do
blocks_user?(user, target) || blocks_user?(user, target) ||
(!User.following?(user, target) && blocks_domain?(user, target)) (blocks_domain?(user, target) and not User.following?(user, target))
end end
def blocks_user?(%User{} = user, %User{} = target) do def blocks_user?(%User{} = user, %User{} = target) do
@ -1619,8 +1612,7 @@ defmodule Pleroma.User do
|> set_cache() |> set_cache()
end end
# AP style def public_key(%{public_key: public_key_pem}) when is_binary(public_key_pem) do
def public_key(%{source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pem}}}) do
key = key =
public_key_pem public_key_pem
|> :public_key.pem_decode() |> :public_key.pem_decode()
@ -1630,7 +1622,7 @@ defmodule Pleroma.User do
{:ok, key} {:ok, key}
end end
def public_key(_), do: {:error, "not found key"} def public_key(_), do: {:error, "key not found"}
def get_public_key_for_ap_id(ap_id) do def get_public_key_for_ap_id(ap_id) do
with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id), with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id),
@ -1641,17 +1633,6 @@ defmodule Pleroma.User do
end end
end end
defp blank?(""), do: nil
defp blank?(n), do: n
def insert_or_update_user(data) do
data
|> Map.put(:name, blank?(data[:name]) || data[:nickname])
|> remote_user_creation()
|> Repo.insert(on_conflict: {:replace_all_except, [:id]}, conflict_target: :nickname)
|> set_cache()
end
def ap_enabled?(%User{local: true}), do: true def ap_enabled?(%User{local: true}), do: true
def ap_enabled?(%User{ap_enabled: ap_enabled}), do: ap_enabled def ap_enabled?(%User{ap_enabled: ap_enabled}), do: ap_enabled
def ap_enabled?(_), do: false def ap_enabled?(_), do: false
@ -1960,12 +1941,6 @@ defmodule Pleroma.User do
|> update_and_set_cache() |> update_and_set_cache()
end end
def update_source_data(user, source_data) do
user
|> cast(%{source_data: source_data}, [:source_data])
|> update_and_set_cache()
end
def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do
%{ %{
admin: is_admin, admin: is_admin,
@ -1973,21 +1948,6 @@ defmodule Pleroma.User do
} }
end end
# ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``.
# For example: [{"name": "Pronoun", "value": "she/her"}, …]
def fields(%{fields: nil, source_data: %{"attachment" => attachment}}) do
limit = Pleroma.Config.get([:instance, :max_remote_account_fields], 0)
attachment
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|> Enum.take(limit)
end
def fields(%{fields: nil}), do: []
def fields(%{fields: fields}), do: fields
def validate_fields(changeset, remote? \\ false) do def validate_fields(changeset, remote? \\ false) do
limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
limit = Pleroma.Config.get([:instance, limit_name], 0) limit = Pleroma.Config.get([:instance, limit_name], 0)
@ -2175,9 +2135,7 @@ defmodule Pleroma.User do
# - display name # - display name
def sanitize_html(%User{} = user, filter) do def sanitize_html(%User{} = user, filter) do
fields = fields =
user Enum.map(user.fields, fn %{"name" => name, "value" => value} ->
|> User.fields()
|> Enum.map(fn %{"name" => name, "value" => value} ->
%{ %{
"name" => name, "name" => name,
"value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly) "value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)

View file

@ -148,7 +148,7 @@ defmodule Pleroma.User.Query do
as: :relationships, as: :relationships,
on: r.following_id == ^id and r.follower_id == u.id on: r.following_id == ^id and r.follower_id == u.id
) )
|> where([relationships: r], r.state == "accept") |> where([relationships: r], r.state == ^:follow_accept)
end end
defp compose_query({:friends, %User{id: id}}, query) do defp compose_query({:friends, %User{id: id}}, query) do
@ -158,7 +158,7 @@ defmodule Pleroma.User.Query do
as: :relationships, as: :relationships,
on: r.following_id == u.id and r.follower_id == ^id on: r.following_id == u.id and r.follower_id == ^id
) )
|> where([relationships: r], r.state == "accept") |> where([relationships: r], r.state == ^:follow_accept)
end end
defp compose_query({:recipients_from_activity, to}, query) do defp compose_query({:recipients_from_activity, to}, query) do
@ -173,7 +173,7 @@ defmodule Pleroma.User.Query do
) )
|> where( |> where(
[u, following: f, relationships: r], [u, following: f, relationships: r],
u.ap_id in ^to or (f.follower_address in ^to and r.state == "accept") u.ap_id in ^to or (f.follower_address in ^to and r.state == ^:follow_accept)
) )
|> distinct(true) |> distinct(true)
end end

View file

@ -8,6 +8,7 @@ defmodule Pleroma.UserRelationship do
import Ecto.Changeset import Ecto.Changeset
import Ecto.Query import Ecto.Query
alias Ecto.Changeset
alias Pleroma.FollowingRelationship alias Pleroma.FollowingRelationship
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
@ -16,12 +17,12 @@ defmodule Pleroma.UserRelationship do
schema "user_relationships" do schema "user_relationships" do
belongs_to(:source, User, type: FlakeId.Ecto.CompatType) belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
belongs_to(:target, User, type: FlakeId.Ecto.CompatType) belongs_to(:target, User, type: FlakeId.Ecto.CompatType)
field(:relationship_type, UserRelationshipTypeEnum) field(:relationship_type, Pleroma.UserRelationship.Type)
timestamps(updated_at: false) timestamps(updated_at: false)
end end
for relationship_type <- Keyword.keys(UserRelationshipTypeEnum.__enum_map__()) do for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do
# `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`, # `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`,
# `def create_notification_mute/2`, `def create_inverse_subscription/2` # `def create_notification_mute/2`, `def create_inverse_subscription/2`
def unquote(:"create_#{relationship_type}")(source, target), def unquote(:"create_#{relationship_type}")(source, target),
@ -40,7 +41,7 @@ defmodule Pleroma.UserRelationship do
def user_relationship_types, do: Keyword.keys(user_relationship_mappings()) def user_relationship_types, do: Keyword.keys(user_relationship_mappings())
def user_relationship_mappings, do: UserRelationshipTypeEnum.__enum_map__() def user_relationship_mappings, do: Pleroma.UserRelationship.Type.__enum_map__()
def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do
user_relationship user_relationship
@ -129,17 +130,27 @@ defmodule Pleroma.UserRelationship do
end end
@doc ":relationships option for StatusView / AccountView / NotificationView" @doc ":relationships option for StatusView / AccountView / NotificationView"
def view_relationships_option(nil = _reading_user, _actors) do def view_relationships_option(reading_user, actors, opts \\ [])
def view_relationships_option(nil = _reading_user, _actors, _opts) do
%{user_relationships: [], following_relationships: []} %{user_relationships: [], following_relationships: []}
end end
def view_relationships_option(%User{} = reading_user, actors) do def view_relationships_option(%User{} = reading_user, actors, opts) do
{source_to_target_rel_types, target_to_source_rel_types} =
if opts[:source_mutes_only] do
# This option is used for rendering statuses (FE needs `muted` flag for each one anyways)
{[:mute], []}
else
{[:block, :mute, :notification_mute, :reblog_mute], [:block, :inverse_subscription]}
end
user_relationships = user_relationships =
UserRelationship.dictionary( UserRelationship.dictionary(
[reading_user], [reading_user],
actors, actors,
[:block, :mute, :notification_mute, :reblog_mute], source_to_target_rel_types,
[:block, :inverse_subscription] target_to_source_rel_types
) )
following_relationships = FollowingRelationship.all_between_user_sets([reading_user], actors) following_relationships = FollowingRelationship.all_between_user_sets([reading_user], actors)
@ -147,16 +158,14 @@ defmodule Pleroma.UserRelationship do
%{user_relationships: user_relationships, following_relationships: following_relationships} %{user_relationships: user_relationships, following_relationships: following_relationships}
end end
defp validate_not_self_relationship(%Ecto.Changeset{} = changeset) do defp validate_not_self_relationship(%Changeset{} = changeset) do
changeset changeset
|> validate_change(:target_id, fn _, target_id -> |> validate_source_id_target_id_inequality()
if target_id == get_field(changeset, :source_id) do |> validate_target_id_source_id_inequality()
[target_id: "can't be equal to source_id"] end
else
[] defp validate_source_id_target_id_inequality(%Changeset{} = changeset) do
end validate_change(changeset, :source_id, fn _, source_id ->
end)
|> validate_change(:source_id, fn _, source_id ->
if source_id == get_field(changeset, :target_id) do if source_id == get_field(changeset, :target_id) do
[source_id: "can't be equal to target_id"] [source_id: "can't be equal to target_id"]
else else
@ -164,4 +173,14 @@ defmodule Pleroma.UserRelationship do
end end
end) end)
end end
defp validate_target_id_source_id_inequality(%Changeset{} = changeset) do
validate_change(changeset, :target_id, fn _, target_id ->
if target_id == get_field(changeset, :source_id) do
[target_id: "can't be equal to source_id"]
else
[]
end
end)
end
end end

View file

@ -721,7 +721,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
end end
defp fetch_activities_for_context_query(context, opts) do def fetch_activities_for_context_query(context, opts) do
public = [Constants.as_public()] public = [Constants.as_public()]
recipients = recipients =
@ -1427,19 +1427,41 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end) |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end) |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
emojis =
data
|> Map.get("tag", [])
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc ->
Map.put(acc, String.trim(name, ":"), url)
end)
locked = data["manuallyApprovesFollowers"] || false locked = data["manuallyApprovesFollowers"] || false
data = Transmogrifier.maybe_fix_user_object(data) data = Transmogrifier.maybe_fix_user_object(data)
discoverable = data["discoverable"] || false discoverable = data["discoverable"] || false
invisible = data["invisible"] || false invisible = data["invisible"] || false
actor_type = data["type"] || "Person" actor_type = data["type"] || "Person"
public_key =
if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do
data["publicKey"]["publicKeyPem"]
else
nil
end
shared_inbox =
if is_map(data["endpoints"]) && is_binary(data["endpoints"]["sharedInbox"]) do
data["endpoints"]["sharedInbox"]
else
nil
end
user_data = %{ user_data = %{
ap_id: data["id"], ap_id: data["id"],
uri: get_actor_url(data["url"]), uri: get_actor_url(data["url"]),
ap_enabled: true, ap_enabled: true,
source_data: data,
banner: banner, banner: banner,
fields: fields, fields: fields,
emoji: emojis,
locked: locked, locked: locked,
discoverable: discoverable, discoverable: discoverable,
invisible: invisible, invisible: invisible,
@ -1449,7 +1471,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
following_address: data["following"], following_address: data["following"],
bio: data["summary"], bio: data["summary"],
actor_type: actor_type, actor_type: actor_type,
also_known_as: Map.get(data, "alsoKnownAs", []) also_known_as: Map.get(data, "alsoKnownAs", []),
public_key: public_key,
inbox: data["inbox"],
shared_inbox: shared_inbox
} }
# nickname can be nil because of virtual actors # nickname can be nil because of virtual actors
@ -1551,11 +1576,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
def make_user_from_ap_id(ap_id) do def make_user_from_ap_id(ap_id) do
if _user = User.get_cached_by_ap_id(ap_id) do user = User.get_cached_by_ap_id(ap_id)
if user && !User.ap_enabled?(user) do
Transmogrifier.upgrade_user_from_ap_id(ap_id) Transmogrifier.upgrade_user_from_ap_id(ap_id)
else else
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
User.insert_or_update_user(data) if user do
user
|> User.remote_user_changeset(data)
|> User.update_and_set_cache()
else
data
|> User.remote_user_changeset()
|> Repo.insert()
|> User.set_cache()
end
else else
e -> {:error, e} e -> {:error, e}
end end

View file

@ -35,6 +35,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
field(:like_count, :integer, default: 0) field(:like_count, :integer, default: 0)
field(:announcement_count, :integer, default: 0) field(:announcement_count, :integer, default: 0)
field(:inRepyTo, :string) field(:inRepyTo, :string)
field(:uri, Types.Uri)
field(:likes, {:array, :string}, default: []) field(:likes, {:array, :string}, default: [])
field(:announcements, {:array, :string}, default: []) field(:announcements, {:array, :string}, default: [])

View file

@ -6,28 +6,18 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.ObjectID do
def cast(object) when is_binary(object) do def cast(object) when is_binary(object) do
# Host has to be present and scheme has to be an http scheme (for now) # Host has to be present and scheme has to be an http scheme (for now)
case URI.parse(object) do case URI.parse(object) do
%URI{host: nil} -> %URI{host: nil} -> :error
:error %URI{host: ""} -> :error
%URI{scheme: scheme} when scheme in ["https", "http"] -> {:ok, object}
%URI{scheme: scheme} when scheme in ["https", "http"] -> _ -> :error
{:ok, object}
_ ->
:error
end end
end end
def cast(%{"id" => object}), do: cast(object) def cast(%{"id" => object}), do: cast(object)
def cast(_) do def cast(_), do: :error
:error
end
def dump(data) do def dump(data), do: {:ok, data}
{:ok, data}
end
def load(data) do def load(data), do: {:ok, data}
{:ok, data}
end
end end

View file

@ -0,0 +1,20 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.Uri do
use Ecto.Type
def type, do: :string
def cast(uri) when is_binary(uri) do
case URI.parse(uri) do
%URI{host: nil} -> :error
%URI{host: ""} -> :error
%URI{scheme: scheme} when scheme in ["https", "http"] -> {:ok, uri}
_ -> :error
end
end
def cast(_), do: :error
def dump(data), do: {:ok, data}
def load(data), do: {:ok, data}
end

View file

@ -141,8 +141,8 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|> Enum.map(& &1.ap_id) |> Enum.map(& &1.ap_id)
end end
defp maybe_use_sharedinbox(%User{source_data: data}), defp maybe_use_sharedinbox(%User{shared_inbox: nil, inbox: inbox}), do: inbox
do: (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] defp maybe_use_sharedinbox(%User{shared_inbox: shared_inbox}), do: shared_inbox
@doc """ @doc """
Determine a user inbox to use based on heuristics. These heuristics Determine a user inbox to use based on heuristics. These heuristics
@ -157,7 +157,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
""" """
def determine_inbox( def determine_inbox(
%Activity{data: activity_data}, %Activity{data: activity_data},
%User{source_data: data} = user %User{inbox: inbox} = user
) do ) do
to = activity_data["to"] || [] to = activity_data["to"] || []
cc = activity_data["cc"] || [] cc = activity_data["cc"] || []
@ -174,7 +174,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
maybe_use_sharedinbox(user) maybe_use_sharedinbox(user)
true -> true ->
data["inbox"] inbox
end end
end end
@ -192,14 +192,13 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
inboxes = inboxes =
recipients recipients
|> Enum.filter(&User.ap_enabled?/1) |> Enum.filter(&User.ap_enabled?/1)
|> Enum.map(fn %{source_data: data} -> data["inbox"] end) |> Enum.map(fn actor -> actor.inbox end)
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end) |> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|> Instances.filter_reachable() |> Instances.filter_reachable()
Repo.checkout(fn -> Repo.checkout(fn ->
Enum.each(inboxes, fn {inbox, unreachable_since} -> Enum.each(inboxes, fn {inbox, unreachable_since} ->
%User{ap_id: ap_id} = %User{ap_id: ap_id} = Enum.find(recipients, fn actor -> actor.inbox == inbox end)
Enum.find(recipients, fn %{source_data: data} -> data["inbox"] == inbox end)
# Get all the recipients on the same host and add them to cc. Otherwise, a remote # Get all the recipients on the same host and add them to cc. Otherwise, a remote
# instance would only accept a first message for the first recipient and ignore the rest. # instance would only accept a first message for the first recipient and ignore the rest.

View file

@ -205,16 +205,46 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Map.put("conversation", context) |> Map.put("conversation", context)
end end
defp add_if_present(map, _key, nil), do: map
defp add_if_present(map, key, value) do
Map.put(map, key, value)
end
def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachment) do def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachment) do
attachments = attachments =
Enum.map(attachment, fn data -> Enum.map(attachment, fn data ->
media_type = data["mediaType"] || data["mimeType"] url =
href = data["url"] || data["href"] cond do
url = [%{"type" => "Link", "mediaType" => media_type, "href" => href}] is_list(data["url"]) -> List.first(data["url"])
is_map(data["url"]) -> data["url"]
true -> nil
end
data media_type =
|> Map.put("mediaType", media_type) cond do
|> Map.put("url", url) is_map(url) && is_binary(url["mediaType"]) -> url["mediaType"]
is_binary(data["mediaType"]) -> data["mediaType"]
is_binary(data["mimeType"]) -> data["mimeType"]
true -> nil
end
href =
cond do
is_map(url) && is_binary(url["href"]) -> url["href"]
is_binary(data["url"]) -> data["url"]
is_binary(data["href"]) -> data["href"]
end
attachment_url =
%{"href" => href}
|> add_if_present("mediaType", media_type)
|> add_if_present("type", Map.get(url || %{}, "type"))
%{"url" => [attachment_url]}
|> add_if_present("mediaType", media_type)
|> add_if_present("type", data["type"])
|> add_if_present("name", data["name"])
end) end)
Map.put(object, "attachment", attachments) Map.put(object, "attachment", attachments)
@ -494,7 +524,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{_, {:ok, follower}} <- {:follow, User.follow(follower, followed)}, {_, {:ok, follower}} <- {:follow, User.follow(follower, followed)},
{_, {:ok, _}} <- {_, {:ok, _}} <-
{:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")}, {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")},
{:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do {:ok, _relationship} <-
FollowingRelationship.update(follower, followed, :follow_accept) do
ActivityPub.accept(%{ ActivityPub.accept(%{
to: [follower.ap_id], to: [follower.ap_id],
actor: followed, actor: followed,
@ -504,7 +535,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
else else
{:user_blocked, true} -> {:user_blocked, true} ->
{:ok, _} = Utils.update_follow_state_for_all(activity, "reject") {:ok, _} = Utils.update_follow_state_for_all(activity, "reject")
{:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject") {:ok, _relationship} = FollowingRelationship.update(follower, followed, :follow_reject)
ActivityPub.reject(%{ ActivityPub.reject(%{
to: [follower.ap_id], to: [follower.ap_id],
@ -515,7 +546,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:follow, {:error, _}} -> {:follow, {:error, _}} ->
{:ok, _} = Utils.update_follow_state_for_all(activity, "reject") {:ok, _} = Utils.update_follow_state_for_all(activity, "reject")
{:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject") {:ok, _relationship} = FollowingRelationship.update(follower, followed, :follow_reject)
ActivityPub.reject(%{ ActivityPub.reject(%{
to: [follower.ap_id], to: [follower.ap_id],
@ -525,7 +556,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
}) })
{:user_locked, true} -> {:user_locked, true} ->
{:ok, _relationship} = FollowingRelationship.update(follower, followed, "pending") {:ok, _relationship} = FollowingRelationship.update(follower, followed, :follow_pending)
:noop :noop
end end
@ -545,7 +576,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, follow_activity} <- get_follow_activity(follow_object, followed), {:ok, follow_activity} <- get_follow_activity(follow_object, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
{:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept) do
ActivityPub.accept(%{ ActivityPub.accept(%{
to: follow_activity.data["to"], to: follow_activity.data["to"],
type: "Accept", type: "Accept",
@ -568,7 +599,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, follow_activity} <- get_follow_activity(follow_object, followed), {:ok, follow_activity} <- get_follow_activity(follow_object, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
{:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"), {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject),
{:ok, activity} <- {:ok, activity} <-
ActivityPub.reject(%{ ActivityPub.reject(%{
to: follow_activity.data["to"], to: follow_activity.data["to"],
@ -680,7 +711,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object) {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
actor actor
|> User.upgrade_changeset(new_user_data, true) |> User.remote_user_changeset(new_user_data)
|> User.update_and_set_cache() |> User.update_and_set_cache()
ActivityPub.update(%{ ActivityPub.update(%{
@ -1129,7 +1160,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def take_emoji_tags(%User{emoji: emoji}) do def take_emoji_tags(%User{emoji: emoji}) do
emoji emoji
|> Enum.flat_map(&Map.to_list/1) |> Map.to_list()
|> Enum.map(&build_emoji_tag/1) |> Enum.map(&build_emoji_tag/1)
end end
@ -1223,12 +1254,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def upgrade_user_from_ap_id(ap_id) do def upgrade_user_from_ap_id(ap_id) do
with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id), with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id),
{:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id), {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id),
already_ap <- User.ap_enabled?(user), {:ok, user} <- update_user(user, data) do
{:ok, user} <- upgrade_user(user, data) do TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id})
if not already_ap do
TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id})
end
{:ok, user} {:ok, user}
else else
%User{} = user -> {:ok, user} %User{} = user -> {:ok, user}
@ -1236,9 +1263,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
end end
defp upgrade_user(user, data) do defp update_user(user, data) do
user user
|> User.upgrade_changeset(data, true) |> User.remote_user_changeset(data)
|> User.update_and_set_cache() |> User.update_and_set_cache()
end end

View file

@ -79,10 +79,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
emoji_tags = Transmogrifier.take_emoji_tags(user) emoji_tags = Transmogrifier.take_emoji_tags(user)
fields = fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
user
|> User.fields()
|> Enum.map(&Map.put(&1, "type", "PropertyValue"))
%{ %{
"id" => user.ap_id, "id" => user.ap_id,
@ -103,7 +100,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
}, },
"endpoints" => endpoints, "endpoints" => endpoints,
"attachment" => fields, "attachment" => fields,
"tag" => (user.source_data["tag"] || []) ++ emoji_tags, "tag" => emoji_tags,
"discoverable" => user.discoverable "discoverable" => user.discoverable
} }
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user)) |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))

View file

@ -27,7 +27,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
alias Pleroma.Web.AdminAPI.Search alias Pleroma.Web.AdminAPI.Search
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Endpoint alias Pleroma.Web.Endpoint
alias Pleroma.Web.MastodonAPI.AppView
alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.OAuth.App
alias Pleroma.Web.Router alias Pleroma.Web.Router
require Logger require Logger
@ -258,7 +260,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
conn conn
|> put_view(Pleroma.Web.AdminAPI.StatusView) |> put_view(Pleroma.Web.AdminAPI.StatusView)
|> render("index.json", %{activities: activities, as: :activity}) |> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
end end
def list_user_statuses(conn, %{"nickname" => nickname} = params) do def list_user_statuses(conn, %{"nickname" => nickname} = params) do
@ -277,7 +279,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
conn conn
|> put_view(StatusView) |> put_view(StatusView)
|> render("index.json", %{activities: activities, as: :activity}) |> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
else else
_ -> {:error, :not_found} _ -> {:error, :not_found}
end end
@ -576,9 +578,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
@doc "Sends registration invite via email" @doc "Sends registration invite via email"
def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
with true <- with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
Config.get([:instance, :invites_enabled]) && {_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
!Config.get([:instance, :registrations_open]),
{:ok, invite_token} <- UserInviteToken.create_invite(), {:ok, invite_token} <- UserInviteToken.create_invite(),
email <- email <-
Pleroma.Emails.UserEmail.user_invitation_email( Pleroma.Emails.UserEmail.user_invitation_email(
@ -589,6 +590,18 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
), ),
{:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
json_response(conn, :no_content, "") json_response(conn, :no_content, "")
else
{:registrations_open, _} ->
errors(
conn,
{:error, "To send invites you need to set the `registrations_open` option to false."}
)
{:invites_enabled, _} ->
errors(
conn,
{:error, "To send invites you need to set the `invites_enabled` option to true."}
)
end end
end end
@ -801,7 +814,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
conn conn
|> put_view(Pleroma.Web.AdminAPI.StatusView) |> put_view(Pleroma.Web.AdminAPI.StatusView)
|> render("index.json", %{activities: activities, as: :activity}) |> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
end end
def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
@ -903,16 +916,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end) end)
|> List.flatten() |> List.flatten()
response = %{configs: merged} json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
response =
if Restarter.Pleroma.need_reboot?() do
Map.put(response, :need_reboot, true)
else
response
end
json(conn, response)
end end
end end
@ -939,28 +943,22 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
Config.TransferTask.load_and_update_env(deleted, false) Config.TransferTask.load_and_update_env(deleted, false)
need_reboot? = if !Restarter.Pleroma.need_reboot?() do
Restarter.Pleroma.need_reboot?() || changed_reboot_settings? =
Enum.any?(updated, fn config -> (updated ++ deleted)
|> Enum.any?(fn config ->
group = ConfigDB.from_string(config.group) group = ConfigDB.from_string(config.group)
key = ConfigDB.from_string(config.key) key = ConfigDB.from_string(config.key)
value = ConfigDB.from_binary(config.value) value = ConfigDB.from_binary(config.value)
Config.TransferTask.pleroma_need_restart?(group, key, value) Config.TransferTask.pleroma_need_restart?(group, key, value)
end) end)
response = %{configs: updated} if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
end
response =
if need_reboot? do
Restarter.Pleroma.need_reboot()
Map.put(response, :need_reboot, need_reboot?)
else
response
end
conn conn
|> put_view(ConfigView) |> put_view(ConfigView)
|> render("index.json", response) |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
end end
end end
@ -972,6 +970,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end end
end end
def need_reboot(conn, _params) do
json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
end
defp configurable_from_database(conn) do defp configurable_from_database(conn) do
if Config.get(:configurable_from_database) do if Config.get(:configurable_from_database) do
:ok :ok
@ -1017,6 +1019,83 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
conn |> json("") conn |> json("")
end end
def oauth_app_create(conn, params) do
params =
if params["name"] do
Map.put(params, "client_name", params["name"])
else
params
end
result =
case App.create(params) do
{:ok, app} ->
AppView.render("show.json", %{app: app, admin: true})
{:error, changeset} ->
App.errors(changeset)
end
json(conn, result)
end
def oauth_app_update(conn, params) do
params =
if params["name"] do
Map.put(params, "client_name", params["name"])
else
params
end
with {:ok, app} <- App.update(params) do
json(conn, AppView.render("show.json", %{app: app, admin: true}))
else
{:error, changeset} ->
json(conn, App.errors(changeset))
nil ->
json_response(conn, :bad_request, "")
end
end
def oauth_app_list(conn, params) do
{page, page_size} = page_params(params)
search_params = %{
client_name: params["name"],
client_id: params["client_id"],
page: page,
page_size: page_size
}
search_params =
if Map.has_key?(params, "trusted") do
Map.put(search_params, :trusted, params["trusted"])
else
search_params
end
with {:ok, apps, count} <- App.search(search_params) do
json(
conn,
AppView.render("index.json",
apps: apps,
count: count,
page_size: page_size,
admin: true
)
)
end
end
def oauth_app_delete(conn, params) do
with {:ok, _app} <- App.destroy(params["id"]) do
json_response(conn, :no_content, "")
else
_ -> json_response(conn, :bad_request, "")
end
end
def stats(conn, _) do def stats(conn, _) do
count = Stats.get_status_visibility_count() count = Stats.get_status_visibility_count()

View file

@ -38,7 +38,12 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
actor: merge_account_views(user), actor: merge_account_views(user),
content: content, content: content,
created_at: created_at, created_at: created_at,
statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}), statuses:
StatusView.render("index.json", %{
activities: statuses,
as: :activity,
skip_relationships: false
}),
state: report.data["state"], state: report.data["state"],
notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes}) notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes})
} }

View file

@ -0,0 +1,44 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec do
alias OpenApiSpex.OpenApi
alias Pleroma.Web.Endpoint
alias Pleroma.Web.Router
@behaviour OpenApi
@impl OpenApi
def spec do
%OpenApi{
servers: [
# Populate the Server info from a phoenix endpoint
OpenApiSpex.Server.from_endpoint(Endpoint)
],
info: %OpenApiSpex.Info{
title: "Pleroma",
description: Application.spec(:pleroma, :description) |> to_string(),
version: Application.spec(:pleroma, :vsn) |> to_string()
},
# populate the paths from a phoenix router
paths: OpenApiSpex.Paths.from_router(Router),
components: %OpenApiSpex.Components{
securitySchemes: %{
"oAuth" => %OpenApiSpex.SecurityScheme{
type: "oauth2",
flows: %OpenApiSpex.OAuthFlows{
password: %OpenApiSpex.OAuthFlow{
authorizationUrl: "/oauth/authorize",
tokenUrl: "/oauth/token",
scopes: %{"read" => "read", "write" => "write", "follow" => "follow"}
}
}
}
}
}
}
# discover request/response schemas from path specs
|> OpenApiSpex.resolve_schema_modules()
end
end

View file

@ -0,0 +1,27 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Helpers do
def request_body(description, schema_ref, opts \\ []) do
media_types = ["application/json", "multipart/form-data", "application/x-www-form-urlencoded"]
content =
media_types
|> Enum.map(fn type ->
{type,
%OpenApiSpex.MediaType{
schema: schema_ref,
example: opts[:example],
examples: opts[:examples]
}}
end)
|> Enum.into(%{})
%OpenApiSpex.RequestBody{
description: description,
content: content,
required: opts[:required] || false
}
end
end

View file

@ -0,0 +1,96 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.AppOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Helpers
alias Pleroma.Web.ApiSpec.Schemas.AppCreateRequest
alias Pleroma.Web.ApiSpec.Schemas.AppCreateResponse
@spec open_api_operation(atom) :: Operation.t()
def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
end
@spec create_operation() :: Operation.t()
def create_operation do
%Operation{
tags: ["apps"],
summary: "Create an application",
description: "Create a new application to obtain OAuth2 credentials",
operationId: "AppController.create",
requestBody: Helpers.request_body("Parameters", AppCreateRequest, required: true),
responses: %{
200 => Operation.response("App", "application/json", AppCreateResponse),
422 =>
Operation.response(
"Unprocessable Entity",
"application/json",
%Schema{
type: :object,
description:
"If a required parameter is missing or improperly formatted, the request will fail.",
properties: %{
error: %Schema{type: :string}
},
example: %{
"error" => "Validation failed: Redirect URI must be an absolute URI."
}
}
)
}
}
end
def verify_credentials_operation do
%Operation{
tags: ["apps"],
summary: "Verify your app works",
description: "Confirm that the app's OAuth2 credentials work.",
operationId: "AppController.verify_credentials",
security: [
%{
"oAuth" => ["read"]
}
],
responses: %{
200 =>
Operation.response("App", "application/json", %Schema{
type: :object,
description:
"If the Authorization header was provided with a valid token, you should see your app returned as an Application entity.",
properties: %{
name: %Schema{type: :string},
vapid_key: %Schema{type: :string},
website: %Schema{type: :string, nullable: true}
},
example: %{
"name" => "My App",
"vapid_key" =>
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
"website" => "https://myapp.com/"
}
}),
422 =>
Operation.response(
"Unauthorized",
"application/json",
%Schema{
type: :object,
description:
"If the Authorization header contains an invalid token, is malformed, or is not present, an error will be returned indicating an authorization failure.",
properties: %{
error: %Schema{type: :string}
},
example: %{
"error" => "The access token is invalid."
}
}
)
}
}
end
end

View file

@ -0,0 +1,64 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Helpers
alias Pleroma.Web.ApiSpec.Schemas.DomainBlockRequest
alias Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse
def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
end
def index_operation do
%Operation{
tags: ["domain_blocks"],
summary: "Fetch domain blocks",
description: "View domains the user has blocked.",
security: [%{"oAuth" => ["follow", "read:blocks"]}],
operationId: "DomainBlockController.index",
responses: %{
200 => Operation.response("Domain blocks", "application/json", DomainBlocksResponse)
}
}
end
def create_operation do
%Operation{
tags: ["domain_blocks"],
summary: "Block a domain",
description: """
Block a domain to:
- hide all public posts from it
- hide all notifications from it
- remove all followers from it
- prevent following new users from it (but does not remove existing follows)
""",
operationId: "DomainBlockController.create",
requestBody: Helpers.request_body("Parameters", DomainBlockRequest, required: true),
security: [%{"oAuth" => ["follow", "write:blocks"]}],
responses: %{
200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
}
}
end
def delete_operation do
%Operation{
tags: ["domain_blocks"],
summary: "Unblock a domain",
description: "Remove a domain block, if it exists in the user's array of blocked domains.",
operationId: "DomainBlockController.delete",
requestBody: Helpers.request_body("Parameters", DomainBlockRequest, required: true),
security: [%{"oAuth" => ["follow", "write:blocks"]}],
responses: %{
200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
}
}
end
end

View file

@ -0,0 +1,33 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.AppCreateRequest do
alias OpenApiSpex.Schema
require OpenApiSpex
OpenApiSpex.schema(%{
title: "AppCreateRequest",
description: "POST body for creating an app",
type: :object,
properties: %{
client_name: %Schema{type: :string, description: "A name for your application."},
redirect_uris: %Schema{
type: :string,
description:
"Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter."
},
scopes: %Schema{
type: :string,
description: "Space separated list of scopes. If none is provided, defaults to `read`."
},
website: %Schema{type: :string, description: "A URL to the homepage of your app"}
},
required: [:client_name, :redirect_uris],
example: %{
"client_name" => "My App",
"redirect_uris" => "https://myapp.com/auth/callback",
"website" => "https://myapp.com/"
}
})
end

View file

@ -0,0 +1,33 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.AppCreateResponse do
alias OpenApiSpex.Schema
require OpenApiSpex
OpenApiSpex.schema(%{
title: "AppCreateResponse",
description: "Response schema for an app",
type: :object,
properties: %{
id: %Schema{type: :string},
name: %Schema{type: :string},
client_id: %Schema{type: :string},
client_secret: %Schema{type: :string},
redirect_uri: %Schema{type: :string},
vapid_key: %Schema{type: :string},
website: %Schema{type: :string, nullable: true}
},
example: %{
"id" => "123",
"name" => "My App",
"client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
"client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
"vapid_key" =>
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
"website" => "https://myapp.com/"
}
})
end

View file

@ -0,0 +1,20 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.DomainBlockRequest do
alias OpenApiSpex.Schema
require OpenApiSpex
OpenApiSpex.schema(%{
title: "DomainBlockRequest",
type: :object,
properties: %{
domain: %Schema{type: :string}
},
required: [:domain],
example: %{
"domain" => "facebook.com"
}
})
end

View file

@ -0,0 +1,16 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse do
require OpenApiSpex
alias OpenApiSpex.Schema
OpenApiSpex.schema(%{
title: "DomainBlocksResponse",
description: "Response schema for domain blocks",
type: :array,
items: %Schema{type: :string},
example: ["google.com", "facebook.com"]
})
end

View file

@ -187,7 +187,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
end end
defp preview?(draft) do defp preview?(draft) do
preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params["preview"]) || false preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params["preview"])
%__MODULE__{draft | preview?: preview?} %__MODULE__{draft | preview?: preview?}
end end

View file

@ -45,7 +45,7 @@ defmodule Pleroma.Web.CommonAPI do
with {:ok, follower} <- User.follow(follower, followed), with {:ok, follower} <- User.follow(follower, followed),
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
{:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept"), {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept),
{:ok, _activity} <- {:ok, _activity} <-
ActivityPub.accept(%{ ActivityPub.accept(%{
to: [follower.ap_id], to: [follower.ap_id],
@ -60,7 +60,7 @@ defmodule Pleroma.Web.CommonAPI do
def reject_follow_request(follower, followed) do def reject_follow_request(follower, followed) do
with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"),
{:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"), {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject),
{:ok, _activity} <- {:ok, _activity} <-
ActivityPub.reject(%{ ActivityPub.reject(%{
to: [follower.ap_id], to: [follower.ap_id],
@ -332,26 +332,6 @@ defmodule Pleroma.Web.CommonAPI do
defp maybe_create_activity_expiration(result, _), do: result defp maybe_create_activity_expiration(result, _), do: result
# Updates the emojis for a user based on their profile
def update(user) do
emoji = emoji_from_profile(user)
source_data = Map.put(user.source_data, "tag", emoji)
user =
case User.update_source_data(user, source_data) do
{:ok, user} -> user
_ -> user
end
ActivityPub.update(%{
local: true,
to: [Pleroma.Constants.as_public(), user.follower_address],
cc: [],
actor: user.ap_id,
object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
})
end
def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do
with %Activity{ with %Activity{
actor: ^user_ap_id, actor: ^user_ap_id,

View file

@ -10,7 +10,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Conversation.Participation alias Pleroma.Conversation.Participation
alias Pleroma.Emoji
alias Pleroma.Formatter alias Pleroma.Formatter
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Plugs.AuthenticationPlug alias Pleroma.Plugs.AuthenticationPlug
@ -18,7 +17,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Endpoint
alias Pleroma.Web.MediaProxy alias Pleroma.Web.MediaProxy
require Logger require Logger
@ -175,7 +173,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
"replies" => %{"type" => "Collection", "totalItems" => 0} "replies" => %{"type" => "Collection", "totalItems" => 0}
} }
{note, Map.merge(emoji, Emoji.Formatter.get_emoji_map(option))} {note, Map.merge(emoji, Pleroma.Emoji.Formatter.get_emoji_map(option))}
end) end)
end_time = end_time =
@ -431,19 +429,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
end end
end end
def emoji_from_profile(%User{bio: bio, name: name}) do
[bio, name]
|> Enum.map(&Emoji.Formatter.get_emoji/1)
|> Enum.concat()
|> Enum.map(fn {shortcode, %Emoji{file: path}} ->
%{
"type" => "Emoji",
"icon" => %{"type" => "Image", "url" => "#{Endpoint.url()}#{path}"},
"name" => ":#{shortcode}:"
}
end)
end
def maybe_notify_to_recipients( def maybe_notify_to_recipients(
recipients, recipients,
%Activity{data: %{"to" => to, "type" => _type}} = _activity %Activity{data: %{"to" => to, "type" => _type}} = _activity

View file

@ -5,10 +5,18 @@
defmodule Pleroma.Web.ControllerHelper do defmodule Pleroma.Web.ControllerHelper do
use Pleroma.Web, :controller use Pleroma.Web, :controller
# As in MastoAPI, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html alias Pleroma.Config
# As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
@falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"] @falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
def truthy_param?(blank_value) when blank_value in [nil, ""], do: nil
def truthy_param?(value), do: value not in @falsy_param_values def explicitly_falsy_param?(value), do: value in @falsy_param_values
# Note: `nil` and `""` are considered falsy values in Pleroma
def falsy_param?(value),
do: explicitly_falsy_param?(value) or value in [nil, ""]
def truthy_param?(value), do: not falsy_param?(value)
def json_response(conn, status, json) do def json_response(conn, status, json) do
conn conn
@ -96,4 +104,14 @@ defmodule Pleroma.Web.ControllerHelper do
def put_if_exist(map, _key, nil), do: map def put_if_exist(map, _key, nil), do: map
def put_if_exist(map, key, value), do: Map.put(map, key, value) def put_if_exist(map, key, value), do: Map.put(map, key, value)
@doc "Whether to skip rendering `[:account][:pleroma][:relationship]`for statuses/notifications"
def skip_relationships?(params) do
if Config.get([:extensions, :output_relationships_in_statuses_by_default]) do
false
else
# BREAKING: older PleromaFE versions do not send this param but _do_ expect relationships.
not truthy_param?(params["with_relationships"])
end
end
end end

View file

@ -23,7 +23,7 @@ defmodule Pleroma.Web.Feed.FeedView do
def pub_date(%DateTime{} = date), do: Timex.format!(date, "{RFC822}") def pub_date(%DateTime{} = date), do: Timex.format!(date, "{RFC822}")
def prepare_activity(activity, opts \\ []) do def prepare_activity(activity, opts \\ []) do
object = activity_object(activity) object = Object.normalize(activity)
actor = actor =
if opts[:actor] do if opts[:actor] do
@ -33,7 +33,6 @@ defmodule Pleroma.Web.Feed.FeedView do
%{ %{
activity: activity, activity: activity,
data: Map.get(object, :data), data: Map.get(object, :data),
object: object,
actor: actor actor: actor
} }
end end
@ -68,9 +67,7 @@ defmodule Pleroma.Web.Feed.FeedView do
def last_activity(activities), do: List.last(activities) def last_activity(activities), do: List.last(activities)
def activity_object(activity), do: Object.normalize(activity) def activity_title(%{"content" => content}, opts \\ %{}) do
def activity_title(%{data: %{"content" => content}}, opts \\ %{}) do
content content
|> Pleroma.Web.Metadata.Utils.scrub_html() |> Pleroma.Web.Metadata.Utils.scrub_html()
|> Pleroma.Emoji.Formatter.demojify() |> Pleroma.Emoji.Formatter.demojify()
@ -78,7 +75,7 @@ defmodule Pleroma.Web.Feed.FeedView do
|> escape() |> escape()
end end
def activity_content(%{data: %{"content" => content}}) do def activity_content(%{"content" => content}) do
content content
|> String.replace(~r/[\n\r]/, "") |> String.replace(~r/[\n\r]/, "")
|> escape() |> escape()

View file

@ -17,7 +17,7 @@ defmodule Pleroma.Web.MastoFEController do
when action == :index when action == :index
) )
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index) plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action not in [:index, :manifest])
@doc "GET /web/*path" @doc "GET /web/*path"
def index(%{assigns: %{user: user, token: token}} = conn, _params) def index(%{assigns: %{user: user, token: token}} = conn, _params)

View file

@ -6,7 +6,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, import Pleroma.Web.ControllerHelper,
only: [add_link_headers: 2, truthy_param?: 1, assign_account_by_id: 2, json_response: 3] only: [
add_link_headers: 2,
truthy_param?: 1,
assign_account_by_id: 2,
json_response: 3,
skip_relationships?: 1
]
alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Plugs.RateLimiter alias Pleroma.Plugs.RateLimiter
@ -15,10 +21,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.ListView alias Pleroma.Web.MastodonAPI.ListView
alias Pleroma.Web.MastodonAPI.MastodonAPI alias Pleroma.Web.MastodonAPI.MastodonAPI
alias Pleroma.Web.MastodonAPI.MastodonAPIController
alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.OAuth.Token alias Pleroma.Web.OAuth.Token
alias Pleroma.Web.TwitterAPI.TwitterAPI alias Pleroma.Web.TwitterAPI.TwitterAPI
plug(:skip_plug, OAuthScopesPlug when action == :identity_proofs)
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]} %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
@ -95,6 +104,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|> Map.put("fullname", params["fullname"] || nickname) |> Map.put("fullname", params["fullname"] || nickname)
|> Map.put("bio", params["bio"] || "") |> Map.put("bio", params["bio"] || "")
|> Map.put("confirm", params["password"]) |> Map.put("confirm", params["password"])
|> Map.put("trusted_app", app.trusted)
with :ok <- validate_email_param(params), with :ok <- validate_email_param(params),
{:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true), {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
@ -140,9 +150,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
end end
@doc "PATCH /api/v1/accounts/update_credentials" @doc "PATCH /api/v1/accounts/update_credentials"
def update_credentials(%{assigns: %{user: original_user}} = conn, params) do def update_credentials(%{assigns: %{user: user}} = conn, params) do
user = original_user
user_params = user_params =
[ [
:no_rich_text, :no_rich_text,
@ -178,8 +186,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
changeset = User.update_changeset(user, user_params) changeset = User.update_changeset(user, user_params)
with {:ok, user} <- User.update_and_set_cache(changeset) do with {:ok, user} <- User.update_and_set_cache(changeset) do
if original_user != user, do: CommonAPI.update(user)
render(conn, "show.json", user: user, for: user, with_pleroma_settings: true) render(conn, "show.json", user: user, for: user, with_pleroma_settings: true)
else else
_e -> render_error(conn, :forbidden, "Invalid request") _e -> render_error(conn, :forbidden, "Invalid request")
@ -237,7 +243,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
conn conn
|> add_link_headers(activities) |> add_link_headers(activities)
|> put_view(StatusView) |> put_view(StatusView)
|> render("index.json", activities: activities, for: reading_user, as: :activity) |> render("index.json",
activities: activities,
for: reading_user,
as: :activity,
skip_relationships: skip_relationships?(params)
)
else else
_e -> render_error(conn, :not_found, "Can't find user") _e -> render_error(conn, :not_found, "Can't find user")
end end
@ -369,6 +380,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
end end
@doc "GET /api/v1/endorsements" @doc "GET /api/v1/endorsements"
def endorsements(conn, params), def endorsements(conn, params), do: MastodonAPIController.empty_array(conn, params)
do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
@doc "GET /api/v1/identity_proofs"
def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params)
end end

View file

@ -14,17 +14,20 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
action_fallback(Pleroma.Web.MastodonAPI.FallbackController) action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials) plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials)
plug(OpenApiSpex.Plug.CastAndValidate)
@local_mastodon_name "Mastodon-Local" @local_mastodon_name "Mastodon-Local"
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation
@doc "POST /api/v1/apps" @doc "POST /api/v1/apps"
def create(conn, params) do def create(%{body_params: params} = conn, _params) do
scopes = Scopes.fetch_scopes(params, ["read"]) scopes = Scopes.fetch_scopes(params, ["read"])
app_attrs = app_attrs =
params params
|> Map.drop(["scope", "scopes"]) |> Map.take([:client_name, :redirect_uris, :website])
|> Map.put("scopes", scopes) |> Map.put(:scopes, scopes)
with cs <- App.register_changeset(%App{}, app_attrs), with cs <- App.register_changeset(%App{}, app_attrs),
false <- cs.changes[:client_name] == @local_mastodon_name, false <- cs.changes[:client_name] == @local_mastodon_name,

View file

@ -8,6 +8,9 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.User alias Pleroma.User
plug(OpenApiSpex.Plug.CastAndValidate)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DomainBlockOperation
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
%{scopes: ["follow", "read:blocks"]} when action == :index %{scopes: ["follow", "read:blocks"]} when action == :index
@ -26,13 +29,13 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
end end
@doc "POST /api/v1/domain_blocks" @doc "POST /api/v1/domain_blocks"
def create(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do def create(%{assigns: %{user: blocker}, body_params: %{domain: domain}} = conn, _params) do
User.block_domain(blocker, domain) User.block_domain(blocker, domain)
json(conn, %{}) json(conn, %{})
end end
@doc "DELETE /api/v1/domain_blocks" @doc "DELETE /api/v1/domain_blocks"
def delete(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do def delete(%{assigns: %{user: blocker}, body_params: %{domain: domain}} = conn, _params) do
User.unblock_domain(blocker, domain) User.unblock_domain(blocker, domain)
json(conn, %{}) json(conn, %{})
end end

View file

@ -3,21 +3,31 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
@moduledoc """
Contains stubs for unimplemented Mastodon API endpoints.
Note: instead of routing directly to this controller's action,
it's preferable to define an action in relevant (non-generic) controller,
set up OAuth rules for it and call this controller's function from it.
"""
use Pleroma.Web, :controller use Pleroma.Web, :controller
require Logger require Logger
plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug when action in [:empty_array, :empty_object])
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
action_fallback(Pleroma.Web.MastodonAPI.FallbackController) action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
# Stubs for unimplemented mastodon api
#
def empty_array(conn, _) do def empty_array(conn, _) do
Logger.debug("Unimplemented, returning an empty array") Logger.debug("Unimplemented, returning an empty array (list)")
json(conn, []) json(conn, [])
end end
def empty_object(conn, _) do def empty_object(conn, _) do
Logger.debug("Unimplemented, returning an empty object") Logger.debug("Unimplemented, returning an empty object (map)")
json(conn, %{}) json(conn, %{})
end end
end end

View file

@ -5,7 +5,7 @@
defmodule Pleroma.Web.MastodonAPI.NotificationController do defmodule Pleroma.Web.MastodonAPI.NotificationController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1]
alias Pleroma.Notification alias Pleroma.Notification
alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.OAuthScopesPlug
@ -45,7 +45,11 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
conn conn
|> add_link_headers(notifications) |> add_link_headers(notifications)
|> render("index.json", notifications: notifications, for: user) |> render("index.json",
notifications: notifications,
for: user,
skip_relationships: skip_relationships?(params)
)
end end
# GET /api/v1/notifications/:id # GET /api/v1/notifications/:id
@ -66,7 +70,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
json(conn, %{}) json(conn, %{})
end end
# POST /api/v1/notifications/dismiss # POST /api/v1/notifications/:id/dismiss
# POST /api/v1/notifications/dismiss (deprecated)
def dismiss(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do def dismiss(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do
with {:ok, _notif} <- Notification.dismiss(user, id) do with {:ok, _notif} <- Notification.dismiss(user, id) do
json(conn, %{}) json(conn, %{})

View file

@ -5,13 +5,14 @@
defmodule Pleroma.Web.MastodonAPI.SearchController do defmodule Pleroma.Web.MastodonAPI.SearchController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [fetch_integer_param: 2, skip_relationships?: 1]
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Plugs.RateLimiter alias Pleroma.Plugs.RateLimiter
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web alias Pleroma.Web
alias Pleroma.Web.ControllerHelper
alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MastodonAPI.StatusView
@ -66,10 +67,11 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
defp search_options(params, user) do defp search_options(params, user) do
[ [
skip_relationships: skip_relationships?(params),
resolve: params["resolve"] == "true", resolve: params["resolve"] == "true",
following: params["following"] == "true", following: params["following"] == "true",
limit: ControllerHelper.fetch_integer_param(params, "limit"), limit: fetch_integer_param(params, "limit"),
offset: ControllerHelper.fetch_integer_param(params, "offset"), offset: fetch_integer_param(params, "offset"),
type: params["type"], type: params["type"],
author: get_author(params), author: get_author(params),
for_user: user for_user: user
@ -79,12 +81,24 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
defp resource_search(_, "accounts", query, options) do defp resource_search(_, "accounts", query, options) do
accounts = with_fallback(fn -> User.search(query, options) end) accounts = with_fallback(fn -> User.search(query, options) end)
AccountView.render("index.json", users: accounts, for: options[:for_user], as: :user)
AccountView.render("index.json",
users: accounts,
for: options[:for_user],
as: :user,
skip_relationships: false
)
end end
defp resource_search(_, "statuses", query, options) do defp resource_search(_, "statuses", query, options) do
statuses = with_fallback(fn -> Activity.search(options[:for_user], query, options) end) statuses = with_fallback(fn -> Activity.search(options[:for_user], query, options) end)
StatusView.render("index.json", activities: statuses, for: options[:for_user], as: :activity)
StatusView.render("index.json",
activities: statuses,
for: options[:for_user],
as: :activity,
skip_relationships: options[:skip_relationships]
)
end end
defp resource_search(:v2, "hashtags", query, _options) do defp resource_search(:v2, "hashtags", query, _options) do

View file

@ -5,7 +5,8 @@
defmodule Pleroma.Web.MastodonAPI.StatusController do defmodule Pleroma.Web.MastodonAPI.StatusController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [try_render: 3, add_link_headers: 2] import Pleroma.Web.ControllerHelper,
only: [try_render: 3, add_link_headers: 2, skip_relationships?: 1]
require Ecto.Query require Ecto.Query
@ -101,7 +102,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
`ids` query param is required `ids` query param is required
""" """
def index(%{assigns: %{user: user}} = conn, %{"ids" => ids}) do def index(%{assigns: %{user: user}} = conn, %{"ids" => ids} = params) do
limit = 100 limit = 100
activities = activities =
@ -110,7 +111,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|> Activity.all_by_ids_with_object() |> Activity.all_by_ids_with_object()
|> Enum.filter(&Visibility.visible_for_user?(&1, user)) |> Enum.filter(&Visibility.visible_for_user?(&1, user))
render(conn, "index.json", activities: activities, for: user, as: :activity) render(conn, "index.json",
activities: activities,
for: user,
as: :activity,
skip_relationships: skip_relationships?(params)
)
end end
@doc """ @doc """
@ -360,7 +366,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
conn conn
|> add_link_headers(activities) |> add_link_headers(activities)
|> render("index.json", activities: activities, for: user, as: :activity) |> render("index.json",
activities: activities,
for: user,
as: :activity,
skip_relationships: skip_relationships?(params)
)
end end
@doc "GET /api/v1/bookmarks" @doc "GET /api/v1/bookmarks"
@ -378,6 +389,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
conn conn
|> add_link_headers(bookmarks) |> add_link_headers(bookmarks)
|> render("index.json", %{activities: activities, for: user, as: :activity}) |> render("index.json",
activities: activities,
for: user,
as: :activity,
skip_relationships: skip_relationships?(params)
)
end end
end end

View file

@ -5,10 +5,13 @@
defmodule Pleroma.Web.MastodonAPI.SuggestionController do defmodule Pleroma.Web.MastodonAPI.SuggestionController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
alias Pleroma.Plugs.OAuthScopesPlug
require Logger require Logger
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :index)
@doc "GET /api/v1/suggestions" @doc "GET /api/v1/suggestions"
def index(conn, _) do def index(conn, params),
json(conn, []) do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
end
end end

View file

@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, import Pleroma.Web.ControllerHelper,
only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1] only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1, skip_relationships?: 1]
alias Pleroma.Pagination alias Pleroma.Pagination
alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.OAuthScopesPlug
@ -14,9 +14,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
# TODO: Replace with a macro when there is a Phoenix release with # TODO: Replace with a macro when there is a Phoenix release with the following commit in it:
# https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e # https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e
# in it
plug(RateLimiter, [name: :timeline, bucket_name: :direct_timeline] when action == :direct) plug(RateLimiter, [name: :timeline, bucket_name: :direct_timeline] when action == :direct)
plug(RateLimiter, [name: :timeline, bucket_name: :public_timeline] when action == :public) plug(RateLimiter, [name: :timeline, bucket_name: :public_timeline] when action == :public)
@ -49,7 +48,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
conn conn
|> add_link_headers(activities) |> add_link_headers(activities)
|> render("index.json", activities: activities, for: user, as: :activity) |> render("index.json",
activities: activities,
for: user,
as: :activity,
skip_relationships: skip_relationships?(params)
)
end end
# GET /api/v1/timelines/direct # GET /api/v1/timelines/direct
@ -68,7 +72,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
conn conn
|> add_link_headers(activities) |> add_link_headers(activities)
|> render("index.json", activities: activities, for: user, as: :activity) |> render("index.json",
activities: activities,
for: user,
as: :activity,
skip_relationships: skip_relationships?(params)
)
end end
# GET /api/v1/timelines/public # GET /api/v1/timelines/public
@ -95,7 +104,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
conn conn
|> add_link_headers(activities, %{"local" => local_only}) |> add_link_headers(activities, %{"local" => local_only})
|> render("index.json", activities: activities, for: user, as: :activity) |> render("index.json",
activities: activities,
for: user,
as: :activity,
skip_relationships: skip_relationships?(params)
)
else else
render_error(conn, :unauthorized, "authorization required for timeline view") render_error(conn, :unauthorized, "authorization required for timeline view")
end end
@ -140,7 +154,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
conn conn
|> add_link_headers(activities, %{"local" => local_only}) |> add_link_headers(activities, %{"local" => local_only})
|> render("index.json", activities: activities, for: user, as: :activity) |> render("index.json",
activities: activities,
for: user,
as: :activity,
skip_relationships: skip_relationships?(params)
)
end end
# GET /api/v1/timelines/list/:list_id # GET /api/v1/timelines/list/:list_id
@ -164,7 +183,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|> ActivityPub.fetch_activities_bounded(following, params) |> ActivityPub.fetch_activities_bounded(following, params)
|> Enum.reverse() |> Enum.reverse()
render(conn, "index.json", activities: activities, for: user, as: :activity) render(conn, "index.json",
activities: activities,
for: user,
as: :activity,
skip_relationships: skip_relationships?(params)
)
else else
_e -> render_error(conn, :forbidden, "Error.") _e -> render_error(conn, :forbidden, "Error.")
end end

View file

@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
def render("index.json", %{users: users} = opts) do def render("index.json", %{users: users} = opts) do
reading_user = opts[:for] reading_user = opts[:for]
# Note: :skip_relationships option is currently intentionally not supported for accounts
relationships_opt = relationships_opt =
cond do cond do
Map.has_key?(opts, :relationships) -> Map.has_key?(opts, :relationships) ->
@ -73,7 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
followed_by = followed_by =
if following_relationships do if following_relationships do
case FollowingRelationship.find(following_relationships, target, reading_user) do case FollowingRelationship.find(following_relationships, target, reading_user) do
%{state: "accept"} -> true %{state: :follow_accept} -> true
_ -> false _ -> false
end end
else else
@ -83,7 +84,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
# NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags # NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags
%{ %{
id: to_string(target.id), id: to_string(target.id),
following: follow_state == "accept", following: follow_state == :follow_accept,
followed_by: followed_by, followed_by: followed_by,
blocking: blocking:
UserRelationship.exists?( UserRelationship.exists?(
@ -125,7 +126,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
reading_user, reading_user,
&User.subscribed_to?(&2, &1) &User.subscribed_to?(&2, &1)
), ),
requested: follow_state == "pending", requested: follow_state == :follow_pending,
domain_blocking: User.blocks_domain?(reading_user, target), domain_blocking: User.blocks_domain?(reading_user, target),
showing_reblogs: showing_reblogs:
not UserRelationship.exists?( not UserRelationship.exists?(
@ -180,23 +181,25 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
bot = user.actor_type in ["Application", "Service"] bot = user.actor_type in ["Application", "Service"]
emojis = emojis =
(user.source_data["tag"] || []) Enum.map(user.emoji, fn {shortcode, url} ->
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
%{ %{
"shortcode" => String.trim(name, ":"), "shortcode" => shortcode,
"url" => MediaProxy.url(url), "url" => url,
"static_url" => MediaProxy.url(url), "static_url" => url,
"visible_in_picker" => false "visible_in_picker" => false
} }
end) end)
relationship = relationship =
render("relationship.json", %{ if opts[:skip_relationships] do
user: opts[:for], %{}
target: user, else
relationships: opts[:relationships] render("relationship.json", %{
}) user: opts[:for],
target: user,
relationships: opts[:relationships]
})
end
%{ %{
id: to_string(user.id), id: to_string(user.id),

View file

@ -7,6 +7,21 @@ defmodule Pleroma.Web.MastodonAPI.AppView do
alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.App
def render("index.json", %{apps: apps, count: count, page_size: page_size, admin: true}) do
%{
apps: render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json", %{admin: true}),
count: count,
page_size: page_size
}
end
def render("show.json", %{admin: true, app: %App{} = app} = assigns) do
"show.json"
|> render(Map.delete(assigns, :admin))
|> Map.put(:trusted, app.trusted)
|> Map.put(:id, app.id)
end
def render("show.json", %{app: %App{} = app}) do def render("show.json", %{app: %App{} = app}) do
%{ %{
id: app.id |> to_string, id: app.id |> to_string,

View file

@ -51,14 +51,15 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|> Enum.filter(& &1) |> Enum.filter(& &1)
|> Kernel.++(move_activities_targets) |> Kernel.++(move_activities_targets)
UserRelationship.view_relationships_option(reading_user, actors) UserRelationship.view_relationships_option(reading_user, actors,
source_mutes_only: opts[:skip_relationships]
)
end end
opts = %{ opts =
for: reading_user, opts
parent_activities: parent_activities, |> Map.put(:parent_activities, parent_activities)
relationships: relationships_opt |> Map.put(:relationships, relationships_opt)
}
safe_render_many(notifications, NotificationView, "show.json", opts) safe_render_many(notifications, NotificationView, "show.json", opts)
end end
@ -82,12 +83,16 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
mastodon_type = Activity.mastodon_notification_type(activity) mastodon_type = Activity.mastodon_notification_type(activity)
render_opts = %{
relationships: opts[:relationships],
skip_relationships: opts[:skip_relationships]
}
with %{id: _} = account <- with %{id: _} = account <-
AccountView.render("show.json", %{ AccountView.render(
user: actor, "show.json",
for: reading_user, Map.merge(render_opts, %{user: actor, for: reading_user})
relationships: opts[:relationships] ) do
}) do
response = %{ response = %{
id: to_string(notification.id), id: to_string(notification.id),
type: mastodon_type, type: mastodon_type,
@ -98,8 +103,6 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
} }
} }
render_opts = %{relationships: opts[:relationships]}
case mastodon_type do case mastodon_type do
"mention" -> "mention" ->
put_status(response, activity, reading_user, render_opts) put_status(response, activity, reading_user, render_opts)
@ -111,6 +114,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
put_status(response, parent_activity_fn.(), reading_user, render_opts) put_status(response, parent_activity_fn.(), reading_user, render_opts)
"move" -> "move" ->
# Note: :skip_relationships option being applied to _account_ rendering (here)
put_target(response, activity, reading_user, render_opts) put_target(response, activity, reading_user, render_opts)
"pleroma:emoji_reaction" -> "pleroma:emoji_reaction" ->

View file

@ -99,7 +99,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
true -> true ->
actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"])) actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"]))
UserRelationship.view_relationships_option(reading_user, actors) UserRelationship.view_relationships_option(reading_user, actors,
source_mutes_only: opts[:skip_relationships]
)
end end
opts = opts =
@ -153,7 +155,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
AccountView.render("show.json", %{ AccountView.render("show.json", %{
user: user, user: user,
for: opts[:for], for: opts[:for],
relationships: opts[:relationships] relationships: opts[:relationships],
skip_relationships: opts[:skip_relationships]
}), }),
in_reply_to_id: nil, in_reply_to_id: nil,
in_reply_to_account_id: nil, in_reply_to_account_id: nil,
@ -301,6 +304,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
_ -> [] _ -> []
end end
# Status muted state (would do 1 request per status unless user mutes are preloaded)
muted = muted =
thread_muted? || thread_muted? ||
UserRelationship.exists?( UserRelationship.exists?(
@ -319,7 +323,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
AccountView.render("show.json", %{ AccountView.render("show.json", %{
user: user, user: user,
for: opts[:for], for: opts[:for],
relationships: opts[:relationships] relationships: opts[:relationships],
skip_relationships: opts[:skip_relationships]
}), }),
in_reply_to_id: reply_to && to_string(reply_to.id), in_reply_to_id: reply_to && to_string(reply_to.id),
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id), in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),

View file

@ -5,6 +5,7 @@
defmodule Pleroma.Web.OAuth.App do defmodule Pleroma.Web.OAuth.App do
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset
import Ecto.Query
alias Pleroma.Repo alias Pleroma.Repo
@type t :: %__MODULE__{} @type t :: %__MODULE__{}
@ -16,14 +17,24 @@ defmodule Pleroma.Web.OAuth.App do
field(:website, :string) field(:website, :string)
field(:client_id, :string) field(:client_id, :string)
field(:client_secret, :string) field(:client_secret, :string)
field(:trusted, :boolean, default: false)
has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all)
has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all)
timestamps() timestamps()
end end
@spec changeset(App.t(), map()) :: Ecto.Changeset.t()
def changeset(struct, params) do
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
end
@spec register_changeset(App.t(), map()) :: Ecto.Changeset.t()
def register_changeset(struct, params \\ %{}) do def register_changeset(struct, params \\ %{}) do
changeset = changeset =
struct struct
|> cast(params, [:client_name, :redirect_uris, :scopes, :website]) |> changeset(params)
|> validate_required([:client_name, :redirect_uris, :scopes]) |> validate_required([:client_name, :redirect_uris, :scopes])
if changeset.valid? do if changeset.valid? do
@ -41,6 +52,21 @@ defmodule Pleroma.Web.OAuth.App do
end end
end end
@spec create(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
def create(params) do
with changeset <- __MODULE__.register_changeset(%__MODULE__{}, params) do
Repo.insert(changeset)
end
end
@spec update(map()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
def update(params) do
with %__MODULE__{} = app <- Repo.get(__MODULE__, params["id"]),
changeset <- changeset(app, params) do
Repo.update(changeset)
end
end
@doc """ @doc """
Gets app by attrs or create new with attrs. Gets app by attrs or create new with attrs.
And updates the scopes if need. And updates the scopes if need.
@ -65,4 +91,58 @@ defmodule Pleroma.Web.OAuth.App do
|> change(%{scopes: scopes}) |> change(%{scopes: scopes})
|> Repo.update() |> Repo.update()
end end
@spec search(map()) :: {:ok, [App.t()], non_neg_integer()}
def search(params) do
query = from(a in __MODULE__)
query =
if params[:client_name] do
from(a in query, where: a.client_name == ^params[:client_name])
else
query
end
query =
if params[:client_id] do
from(a in query, where: a.client_id == ^params[:client_id])
else
query
end
query =
if Map.has_key?(params, :trusted) do
from(a in query, where: a.trusted == ^params[:trusted])
else
query
end
query =
from(u in query,
limit: ^params[:page_size],
offset: ^((params[:page] - 1) * params[:page_size])
)
count = Repo.aggregate(__MODULE__, :count, :id)
{:ok, Repo.all(query), count}
end
@spec destroy(pos_integer()) :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
def destroy(id) do
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
Repo.delete(app)
end
end
@spec errors(Ecto.Changeset.t()) :: map()
def errors(changeset) do
Enum.reduce(changeset.errors, %{}, fn
{:client_name, {error, _}}, acc ->
Map.put(acc, :name, error)
{key, {error, _}}, acc ->
Map.put(acc, key, error)
end)
end
end end

View file

@ -27,6 +27,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
plug(:fetch_flash) plug(:fetch_flash)
plug(RateLimiter, [name: :authentication] when action == :create_authorization) plug(RateLimiter, [name: :authentication] when action == :create_authorization)
plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug)
action_fallback(Pleroma.Web.OAuth.FallbackController) action_fallback(Pleroma.Web.OAuth.FallbackController)
@oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob" @oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob"

View file

@ -15,7 +15,12 @@ defmodule Pleroma.Web.OAuth.Scopes do
Note: `scopes` is used by Mastodon supporting it but sticking to Note: `scopes` is used by Mastodon supporting it but sticking to
OAuth's standard `scope` wherever we control it OAuth's standard `scope` wherever we control it
""" """
@spec fetch_scopes(map(), list()) :: list() @spec fetch_scopes(map() | struct(), list()) :: list()
def fetch_scopes(%Pleroma.Web.ApiSpec.Schemas.AppCreateRequest{scopes: scopes}, default) do
parse_scopes(scopes, default)
end
def fetch_scopes(params, default) do def fetch_scopes(params, default) do
parse_scopes(params["scope"] || params["scopes"], default) parse_scopes(params["scope"] || params["scopes"], default)
end end

View file

@ -6,14 +6,13 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, import Pleroma.Web.ControllerHelper,
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2] only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2, skip_relationships?: 1]
alias Ecto.Changeset alias Ecto.Changeset
alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Plugs.RateLimiter alias Pleroma.Plugs.RateLimiter
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MastodonAPI.StatusView
require Pleroma.Constants require Pleroma.Constants
@ -58,38 +57,32 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
@doc "PATCH /api/v1/pleroma/accounts/update_avatar" @doc "PATCH /api/v1/pleroma/accounts/update_avatar"
def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
{:ok, user} = {:ok, _user} =
user user
|> Changeset.change(%{avatar: nil}) |> Changeset.change(%{avatar: nil})
|> User.update_and_set_cache() |> User.update_and_set_cache()
CommonAPI.update(user)
json(conn, %{url: nil}) json(conn, %{url: nil})
end end
def update_avatar(%{assigns: %{user: user}} = conn, params) do def update_avatar(%{assigns: %{user: user}} = conn, params) do
{:ok, %{data: data}} = ActivityPub.upload(params, type: :avatar) {:ok, %{data: data}} = ActivityPub.upload(params, type: :avatar)
{:ok, user} = user |> Changeset.change(%{avatar: data}) |> User.update_and_set_cache() {:ok, _user} = user |> Changeset.change(%{avatar: data}) |> User.update_and_set_cache()
%{"url" => [%{"href" => href} | _]} = data %{"url" => [%{"href" => href} | _]} = data
CommonAPI.update(user)
json(conn, %{url: href}) json(conn, %{url: href})
end end
@doc "PATCH /api/v1/pleroma/accounts/update_banner" @doc "PATCH /api/v1/pleroma/accounts/update_banner"
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
with {:ok, user} <- User.update_banner(user, %{}) do with {:ok, _user} <- User.update_banner(user, %{}) do
CommonAPI.update(user)
json(conn, %{url: nil}) json(conn, %{url: nil})
end end
end end
def update_banner(%{assigns: %{user: user}} = conn, params) do def update_banner(%{assigns: %{user: user}} = conn, params) do
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner), with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
{:ok, user} <- User.update_banner(user, object.data) do {:ok, _user} <- User.update_banner(user, object.data) do
CommonAPI.update(user)
%{"url" => [%{"href" => href} | _]} = object.data %{"url" => [%{"href" => href} | _]} = object.data
json(conn, %{url: href}) json(conn, %{url: href})
@ -139,7 +132,12 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
conn conn
|> add_link_headers(activities) |> add_link_headers(activities)
|> put_view(StatusView) |> put_view(StatusView)
|> render("index.json", activities: activities, for: for_user, as: :activity) |> render("index.json",
activities: activities,
for: for_user,
as: :activity,
skip_relationships: skip_relationships?(params)
)
end end
@doc "POST /api/v1/pleroma/accounts/:id/subscribe" @doc "POST /api/v1/pleroma/accounts/:id/subscribe"

View file

@ -5,7 +5,7 @@
defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1]
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Conversation.Participation alias Pleroma.Conversation.Participation
@ -34,7 +34,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
%{scopes: ["write:conversations"]} when action == :update_conversation %{scopes: ["write:conversations"]} when action in [:update_conversation, :read_conversations]
) )
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :read_notification) plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :read_notification)
@ -110,12 +110,11 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
end end
def conversation_statuses( def conversation_statuses(
%{assigns: %{user: user}} = conn, %{assigns: %{user: %{id: user_id} = user}} = conn,
%{"id" => participation_id} = params %{"id" => participation_id} = params
) do ) do
with %Participation{} = participation <- with %Participation{user_id: ^user_id} = participation <-
Participation.get(participation_id, preload: [:conversation]), Participation.get(participation_id, preload: [:conversation]) do
true <- user.id == participation.user_id do
params = params =
params params
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
@ -124,13 +123,19 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
activities = activities =
participation.conversation.ap_id participation.conversation.ap_id
|> ActivityPub.fetch_activities_for_context(params) |> ActivityPub.fetch_activities_for_context_query(params)
|> Pleroma.Pagination.fetch_paginated(Map.put(params, "total", false))
|> Enum.reverse() |> Enum.reverse()
conn conn
|> add_link_headers(activities) |> add_link_headers(activities)
|> put_view(StatusView) |> put_view(StatusView)
|> render("index.json", %{activities: activities, for: user, as: :activity}) |> render("index.json",
activities: activities,
for: user,
as: :activity,
skip_relationships: skip_relationships?(params)
)
else else
_error -> _error ->
conn conn
@ -184,13 +189,17 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
end end
end end
def read_notification(%{assigns: %{user: user}} = conn, %{"max_id" => max_id}) do def read_notification(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do
with notifications <- Notification.set_read_up_to(user, max_id) do with notifications <- Notification.set_read_up_to(user, max_id) do
notifications = Enum.take(notifications, 80) notifications = Enum.take(notifications, 80)
conn conn
|> put_view(NotificationView) |> put_view(NotificationView)
|> render("index.json", %{notifications: notifications, for: user}) |> render("index.json",
notifications: notifications,
for: user,
skip_relationships: skip_relationships?(params)
)
end end
end end
end end

View file

@ -64,5 +64,8 @@ defmodule Pleroma.Web.RichMedia.Helpers do
def fetch_data_for_activity(_), do: %{} def fetch_data_for_activity(_), do: %{}
def perform(:fetch, %Activity{} = activity), do: fetch_data_for_activity(activity) def perform(:fetch, %Activity{} = activity) do
fetch_data_for_activity(activity)
:ok
end
end end

View file

@ -29,11 +29,13 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Plugs.SetUserSessionIdPlug) plug(Pleroma.Plugs.SetUserSessionIdPlug)
plug(Pleroma.Plugs.EnsureUserKeyPlug) plug(Pleroma.Plugs.EnsureUserKeyPlug)
plug(Pleroma.Plugs.IdempotencyPlug) plug(Pleroma.Plugs.IdempotencyPlug)
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
end end
pipeline :authenticated_api do pipeline :authenticated_api do
plug(:accepts, ["json"]) plug(:accepts, ["json"])
plug(:fetch_session) plug(:fetch_session)
plug(Pleroma.Plugs.AuthExpectedPlug)
plug(Pleroma.Plugs.OAuthPlug) plug(Pleroma.Plugs.OAuthPlug)
plug(Pleroma.Plugs.BasicAuthDecoderPlug) plug(Pleroma.Plugs.BasicAuthDecoderPlug)
plug(Pleroma.Plugs.UserFetcherPlug) plug(Pleroma.Plugs.UserFetcherPlug)
@ -44,6 +46,7 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Plugs.SetUserSessionIdPlug) plug(Pleroma.Plugs.SetUserSessionIdPlug)
plug(Pleroma.Plugs.EnsureAuthenticatedPlug) plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
plug(Pleroma.Plugs.IdempotencyPlug) plug(Pleroma.Plugs.IdempotencyPlug)
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
end end
pipeline :admin_api do pipeline :admin_api do
@ -61,6 +64,7 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Plugs.EnsureAuthenticatedPlug) plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
plug(Pleroma.Plugs.UserIsAdminPlug) plug(Pleroma.Plugs.UserIsAdminPlug)
plug(Pleroma.Plugs.IdempotencyPlug) plug(Pleroma.Plugs.IdempotencyPlug)
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
end end
pipeline :mastodon_html do pipeline :mastodon_html do
@ -94,10 +98,12 @@ defmodule Pleroma.Web.Router do
pipeline :config do pipeline :config do
plug(:accepts, ["json", "xml"]) plug(:accepts, ["json", "xml"])
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
end end
pipeline :pleroma_api do pipeline :pleroma_api do
plug(:accepts, ["html", "json"]) plug(:accepts, ["html", "json"])
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
end end
pipeline :mailbox_preview do pipeline :mailbox_preview do
@ -198,12 +204,18 @@ defmodule Pleroma.Web.Router do
get("/config", AdminAPIController, :config_show) get("/config", AdminAPIController, :config_show)
post("/config", AdminAPIController, :config_update) post("/config", AdminAPIController, :config_update)
get("/config/descriptions", AdminAPIController, :config_descriptions) get("/config/descriptions", AdminAPIController, :config_descriptions)
get("/need_reboot", AdminAPIController, :need_reboot)
get("/restart", AdminAPIController, :restart) get("/restart", AdminAPIController, :restart)
get("/moderation_log", AdminAPIController, :list_log) get("/moderation_log", AdminAPIController, :list_log)
post("/reload_emoji", AdminAPIController, :reload_emoji) post("/reload_emoji", AdminAPIController, :reload_emoji)
get("/stats", AdminAPIController, :stats) get("/stats", AdminAPIController, :stats)
get("/oauth_app", AdminAPIController, :oauth_app_list)
post("/oauth_app", AdminAPIController, :oauth_app_create)
patch("/oauth_app/:id", AdminAPIController, :oauth_app_update)
delete("/oauth_app/:id", AdminAPIController, :oauth_app_delete)
end end
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
@ -333,7 +345,7 @@ defmodule Pleroma.Web.Router do
get("/accounts/relationships", AccountController, :relationships) get("/accounts/relationships", AccountController, :relationships)
get("/accounts/:id/lists", AccountController, :lists) get("/accounts/:id/lists", AccountController, :lists)
get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array) get("/accounts/:id/identity_proofs", AccountController, :identity_proofs)
get("/follow_requests", FollowRequestController, :index) get("/follow_requests", FollowRequestController, :index)
get("/blocks", AccountController, :blocks) get("/blocks", AccountController, :blocks)
@ -347,9 +359,11 @@ defmodule Pleroma.Web.Router do
get("/notifications", NotificationController, :index) get("/notifications", NotificationController, :index)
get("/notifications/:id", NotificationController, :show) get("/notifications/:id", NotificationController, :show)
post("/notifications/:id/dismiss", NotificationController, :dismiss)
post("/notifications/clear", NotificationController, :clear) post("/notifications/clear", NotificationController, :clear)
post("/notifications/dismiss", NotificationController, :dismiss)
delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple) delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple)
# Deprecated: was removed in Mastodon v3, use `/notifications/:id/dismiss` instead
post("/notifications/dismiss", NotificationController, :dismiss)
get("/scheduled_statuses", ScheduledActivityController, :index) get("/scheduled_statuses", ScheduledActivityController, :index)
get("/scheduled_statuses/:id", ScheduledActivityController, :show) get("/scheduled_statuses/:id", ScheduledActivityController, :show)
@ -500,6 +514,12 @@ defmodule Pleroma.Web.Router do
) )
end end
scope "/api" do
pipe_through(:api)
get("/openapi", OpenApiSpex.Plug.RenderSpec, [])
end
scope "/api", Pleroma.Web, as: :authenticated_twitter_api do scope "/api", Pleroma.Web, as: :authenticated_twitter_api do
pipe_through(:authenticated_api) pipe_through(:authenticated_api)
@ -658,6 +678,17 @@ defmodule Pleroma.Web.Router do
end end
end end
# Test-only routes needed to test action dispatching and plug chain execution
if Pleroma.Config.get(:env) == :test do
scope "/test/authenticated_api", Pleroma.Tests do
pipe_through(:authenticated_api)
for action <- [:skipped_oauth, :performed_oauth, :missed_oauth] do
get("/#{action}", OAuthTestController, action)
end
end
end
scope "/", Pleroma.Web.MongooseIM do scope "/", Pleroma.Web.MongooseIM do
get("/user_exists", MongooseIMController, :user_exists) get("/user_exists", MongooseIMController, :user_exists)
get("/check_password", MongooseIMController, :check_password) get("/check_password", MongooseIMController, :check_password)

View file

@ -18,15 +18,6 @@ defmodule Pleroma.Web.StaticFE.StaticFEView do
@media_types ["image", "audio", "video"] @media_types ["image", "audio", "video"]
def emoji_for_user(%User{} = user) do
user.source_data
|> Map.get("tag", [])
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
{String.trim(name, ":"), url}
end)
end
def fetch_media_type(%{"mediaType" => mediaType}) do def fetch_media_type(%{"mediaType" => mediaType}) do
Utils.fetch_media_type(@media_types, mediaType) Utils.fetch_media_type(@media_types, mediaType)
end end

View file

@ -2,10 +2,10 @@
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<id><%= @data["id"] %></id> <id><%= @data["id"] %></id>
<title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title> <title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
<content type="html"><%= activity_content(@object) %></content> <content type="html"><%= activity_content(@data) %></content>
<published><%= @data["published"] %></published> <published><%= @activity.data["published"] %></published>
<updated><%= @data["published"] %></updated> <updated><%= @activity.data["published"] %></updated>
<ostatus:conversation ref="<%= activity_context(@activity) %>"> <ostatus:conversation ref="<%= activity_context(@activity) %>">
<%= activity_context(@activity) %> <%= activity_context(@activity) %>
</ostatus:conversation> </ostatus:conversation>

View file

@ -2,10 +2,10 @@
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<guid><%= @data["id"] %></guid> <guid><%= @data["id"] %></guid>
<title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title> <title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
<description><%= activity_content(@object) %></description> <description><%= activity_content(@data) %></description>
<pubDate><%= @data["published"] %></pubDate> <pubDate><%= @activity.data["published"] %></pubDate>
<updated><%= @data["published"] %></updated> <updated><%= @activity.data["published"] %></updated>
<ostatus:conversation ref="<%= activity_context(@activity) %>"> <ostatus:conversation ref="<%= activity_context(@activity) %>">
<%= activity_context(@activity) %> <%= activity_context(@activity) %>
</ostatus:conversation> </ostatus:conversation>

View file

@ -1,12 +1,12 @@
<entry> <entry>
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<%= render @view_module, "_tag_author.atom", assigns %> <%= render @view_module, "_tag_author.atom", assigns %>
<id><%= @data["id"] %></id> <id><%= @data["id"] %></id>
<title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title> <title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
<content type="html"><%= activity_content(@object) %></content> <content type="html"><%= activity_content(@data) %></content>
<%= if @activity.local do %> <%= if @activity.local do %>
<link type="application/atom+xml" href='<%= @data["id"] %>' rel="self"/> <link type="application/atom+xml" href='<%= @data["id"] %>' rel="self"/>
@ -15,8 +15,8 @@
<link type="text/html" href='<%= @data["external_url"] %>' rel="alternate"/> <link type="text/html" href='<%= @data["external_url"] %>' rel="alternate"/>
<% end %> <% end %>
<published><%= @data["published"] %></published> <published><%= @activity.data["published"] %></published>
<updated><%= @data["published"] %></updated> <updated><%= @activity.data["published"] %></updated>
<ostatus:conversation ref="<%= activity_context(@activity) %>"> <ostatus:conversation ref="<%= activity_context(@activity) %>">
<%= activity_context(@activity) %> <%= activity_context(@activity) %>
@ -26,7 +26,7 @@
<%= if @data["summary"] do %> <%= if @data["summary"] do %>
<summary><%= @data["summary"] %></summary> <summary><%= @data["summary"] %></summary>
<% end %> <% end %>
<%= for id <- @activity.recipients do %> <%= for id <- @activity.recipients do %>
<%= if id == Pleroma.Constants.as_public() do %> <%= if id == Pleroma.Constants.as_public() do %>
<link rel="mentioned" <link rel="mentioned"
@ -40,7 +40,7 @@
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>
<%= for tag <- @data["tag"] || [] do %> <%= for tag <- @data["tag"] || [] do %>
<category term="<%= tag %>"></category> <category term="<%= tag %>"></category>
<% end %> <% end %>

View file

@ -1,15 +1,14 @@
<item> <item>
<title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title> <title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
<guid isPermalink="true"><%= activity_context(@activity) %></guid> <guid isPermalink="true"><%= activity_context(@activity) %></guid>
<link><%= activity_context(@activity) %></link> <link><%= activity_context(@activity) %></link>
<pubDate><%= pub_date(@data["published"]) %></pubDate> <pubDate><%= pub_date(@activity.data["published"]) %></pubDate>
<description><%= activity_content(@object) %></description> <description><%= activity_content(@data) %></description>
<%= for attachment <- @data["attachment"] || [] do %> <%= for attachment <- @data["attachment"] || [] do %>
<enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>"/> <enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>"/>
<% end %> <% end %>
</item>
</item>

View file

@ -4,7 +4,7 @@
<img src="<%= User.avatar_url(@user) |> MediaProxy.url %>" width="48" height="48" alt=""> <img src="<%= User.avatar_url(@user) |> MediaProxy.url %>" width="48" height="48" alt="">
</div> </div>
<span class="display-name"> <span class="display-name">
<bdi><%= raw (@user.name |> Formatter.emojify(emoji_for_user(@user))) %></bdi> <bdi><%= raw Formatter.emojify(@user.name, @user.emoji) %></bdi>
<span class="nickname"><%= @user.nickname %></span> <span class="nickname"><%= @user.nickname %></span>
</span> </span>
</a> </a>

View file

@ -7,7 +7,7 @@
<input type="hidden" name="profile" value=""> <input type="hidden" name="profile" value="">
<button type="submit" class="collapse">Remote follow</button> <button type="submit" class="collapse">Remote follow</button>
</form> </form>
<%= raw Formatter.emojify(@user.name, emoji_for_user(@user)) %> | <%= raw Formatter.emojify(@user.name, @user.emoji) %> |
<%= link "@#{@user.nickname}@#{Endpoint.host()}", to: (@user.uri || @user.ap_id) %> <%= link "@#{@user.nickname}@#{Endpoint.host()}", to: (@user.uri || @user.ap_id) %>
</h3> </h3>
<p><%= raw @user.bio %></p> <p><%= raw @user.bio %></p>

View file

@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
def register_user(params, opts \\ []) do def register_user(params, opts \\ []) do
token = params["token"] token = params["token"]
trusted_app? = params["trusted_app"]
params = %{ params = %{
nickname: params["nickname"], nickname: params["nickname"],
@ -29,7 +30,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled]) captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled])
# true if captcha is disabled or enabled and valid, false otherwise # true if captcha is disabled or enabled and valid, false otherwise
captcha_ok = captcha_ok =
if not captcha_enabled do if trusted_app? || not captcha_enabled do
:ok :ok
else else
Pleroma.Captcha.validate( Pleroma.Captcha.validate(

View file

@ -15,6 +15,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read) plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read)
plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token])
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
action_fallback(:errors) action_fallback(:errors)

View file

@ -29,11 +29,40 @@ defmodule Pleroma.Web do
import Pleroma.Web.Router.Helpers import Pleroma.Web.Router.Helpers
import Pleroma.Web.TranslationHelpers import Pleroma.Web.TranslationHelpers
alias Pleroma.Plugs.PlugHelper
plug(:set_put_layout) plug(:set_put_layout)
defp set_put_layout(conn, _) do defp set_put_layout(conn, _) do
put_layout(conn, Pleroma.Config.get(:app_layout, "app.html")) put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
end end
# Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain
defp skip_plug(conn, plug_module) do
try do
plug_module.ensure_skippable()
rescue
UndefinedFunctionError ->
raise "#{plug_module} is not skippable. Append `use Pleroma.Web, :plug` to its code."
end
PlugHelper.append_to_skipped_plugs(conn, plug_module)
end
# Here we can apply before-action hooks (e.g. verify whether auth checks were preformed)
defp action(conn, params) do
if Pleroma.Plugs.AuthExpectedPlug.auth_expected?(conn) &&
not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do
conn
|> render_error(
:forbidden,
"Security violation: OAuth scopes check was neither handled nor explicitly skipped."
)
|> halt()
else
super(conn, params)
end
end
end end
end end
@ -96,6 +125,26 @@ defmodule Pleroma.Web do
end end
end end
def plug do
quote do
alias Pleroma.Plugs.PlugHelper
def ensure_skippable, do: :noop
@impl Plug
@doc "If marked as skipped, returns `conn`, and calls `perform/2` otherwise."
def call(%Plug.Conn{} = conn, options) do
if PlugHelper.plug_skipped?(conn, __MODULE__) do
conn
else
conn
|> PlugHelper.append_to_called_plugs(__MODULE__)
|> perform(options)
end
end
end
end
@doc """ @doc """
When used, dispatch to the appropriate controller/view/etc. When used, dispatch to the appropriate controller/view/etc.
""" """

18
mix.exs
View file

@ -37,12 +37,21 @@ defmodule Pleroma.Mixfile do
pleroma: [ pleroma: [
include_executables_for: [:unix], include_executables_for: [:unix],
applications: [ex_syslogger: :load, syslog: :load], applications: [ex_syslogger: :load, syslog: :load],
steps: [:assemble, &copy_files/1, &copy_nginx_config/1] steps: [:assemble, &put_otp_version/1, &copy_files/1, &copy_nginx_config/1]
] ]
] ]
] ]
end end
def put_otp_version(%{path: target_path} = release) do
File.write!(
Path.join([target_path, "OTP_VERSION"]),
Pleroma.OTPVersion.version()
)
release
end
def copy_files(%{path: target_path} = release) do def copy_files(%{path: target_path} = release) do
File.cp_r!("./rel/files", target_path) File.cp_r!("./rel/files", target_path)
release release
@ -108,7 +117,7 @@ defmodule Pleroma.Mixfile do
{:ecto_enum, "~> 1.4"}, {:ecto_enum, "~> 1.4"},
{:ecto_sql, "~> 3.3.2"}, {:ecto_sql, "~> 3.3.2"},
{:postgrex, ">= 0.13.5"}, {:postgrex, ">= 0.13.5"},
{:oban, "~> 0.12.1"}, {:oban, "~> 1.2"},
{:gettext, "~> 0.15"}, {:gettext, "~> 0.15"},
{:comeonin, "~> 4.1.1"}, {:comeonin, "~> 4.1.1"},
{:pbkdf2_elixir, "~> 0.12.3"}, {:pbkdf2_elixir, "~> 0.12.3"},
@ -174,12 +183,13 @@ defmodule Pleroma.Mixfile do
{:flake_id, "~> 0.1.0"}, {:flake_id, "~> 0.1.0"},
{:remote_ip, {:remote_ip,
git: "https://git.pleroma.social/pleroma/remote_ip.git", git: "https://git.pleroma.social/pleroma/remote_ip.git",
ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"}, ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"},
{:captcha, {:captcha,
git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git",
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"}, ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
{:mox, "~> 0.5", only: :test}, {:mox, "~> 0.5", only: :test},
{:restarter, path: "./restarter"} {:restarter, path: "./restarter"},
{:open_api_spex, "~> 3.6"}
] ++ oauth_deps() ] ++ oauth_deps()
end end

View file

@ -26,7 +26,7 @@
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"},
"ecto": {:hex, :ecto, "3.3.3", "0830bf3aebcbf3d8c1a1811cd581773b6866886c012f52c0f027031fa96a0b53", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "12e368e3c2a2938d7776defaabdae40e82900fc4d8d66120ec1e01dfd8b93c3a"}, "ecto": {:hex, :ecto, "3.4.0", "a7a83ab8359bf816ce729e5e65981ce25b9fc5adfc89c2ea3980f4fed0bfd7c1", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "5eed18252f5b5bbadec56a24112b531343507dbe046273133176b12190ce19cc"},
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"}, "ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
"ecto_sql": {:hex, :ecto_sql, "3.3.4", "aa18af12eb875fbcda2f75e608b3bd534ebf020fc4f6448e4672fcdcbb081244", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5eccbdbf92e3c6f213007a82d5dbba4cd9bb659d1a21331f89f408e4c0efd7a8"}, "ecto_sql": {:hex, :ecto_sql, "3.3.4", "aa18af12eb875fbcda2f75e608b3bd534ebf020fc4f6448e4672fcdcbb081244", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5eccbdbf92e3c6f213007a82d5dbba4cd9bb659d1a21331f89f408e4c0efd7a8"},
"esshd": {:hex, :esshd, "0.1.1", "d4dd4c46698093a40a56afecce8a46e246eb35463c457c246dacba2e056f31b5", [:mix], [], "hexpm", "d73e341e3009d390aa36387dc8862860bf9f874c94d9fd92ade2926376f49981"}, "esshd": {:hex, :esshd, "0.1.1", "d4dd4c46698093a40a56afecce8a46e246eb35463c457c246dacba2e056f31b5", [:mix], [], "hexpm", "d73e341e3009d390aa36387dc8862860bf9f874c94d9fd92ade2926376f49981"},
@ -55,7 +55,7 @@
"httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"}, "httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"},
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"}, "inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fdf843bca858203ae1de16da2ee206f53416bbda5dc8c9e78f43243de4bc3afe"}, "jason": {:hex, :jason, "1.2.0", "10043418c42d2493d0ee212d3fddd25d7ffe484380afad769a0a38795938e448", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "116747dbe057794c3a3e4e143b7c8390b29f634e16c78a7f59ba75bfa6852e7f"},
"joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"}, "joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"},
"jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"}, "jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"},
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"}, "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
@ -73,7 +73,8 @@
"myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]}, "myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"}, "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]}, "nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
"oban": {:hex, :oban, "0.12.1", "695e9490c6e0edfca616d80639528e448bd29b3bff7b7dd10a56c79b00a5d7fb", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c1d58d69b8b5a86e7167abbb8cc92764a66f25f12f6172052595067fc6a30a17"}, "oban": {:hex, :oban, "1.2.0", "7cca94d341be43d220571e28f69131c4afc21095b25257397f50973d3fc59b07", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ba5f8b3f7d76967b3e23cf8014f6a13e4ccb33431e4808f036709a7f822362ee"},
"open_api_spex": {:hex, :open_api_spex, "3.6.0", "64205aba9f2607f71b08fd43e3351b9c5e9898ec5ef49fc0ae35890da502ade9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "126ba3473966277132079cb1d5bf1e3df9e36fe2acd00166e75fd125cecb59c5"},
"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, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "hexpm", "595d09db74cb093b1903381c9de423276a931a2480a46a1a5dc7f932a2a6375b"}, "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "hexpm", "595d09db74cb093b1903381c9de423276a931a2480a46a1a5dc7f932a2a6375b"},
"phoenix": {:hex, :phoenix, "1.4.13", "67271ad69b51f3719354604f4a3f968f83aa61c19199343656c9caee057ff3b8", [: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", "ab765a0feddb81fc62e2116c827b5f068df85159c162bee760745276ad7ddc1b"}, "phoenix": {:hex, :phoenix, "1.4.13", "67271ad69b51f3719354604f4a3f968f83aa61c19199343656c9caee057ff3b8", [: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", "ab765a0feddb81fc62e2116c827b5f068df85159c162bee760745276ad7ddc1b"},
@ -96,7 +97,7 @@
"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"},
"recon": {:hex, :recon, "2.5.0", "2f7fcbec2c35034bade2f9717f77059dc54eb4e929a3049ca7ba6775c0bd66cd", [:mix, :rebar3], [], "hexpm", "72f3840fedd94f06315c523f6cecf5b4827233bed7ae3fe135b2a0ebeab5e196"}, "recon": {:hex, :recon, "2.5.0", "2f7fcbec2c35034bade2f9717f77059dc54eb4e929a3049ca7ba6775c0bd66cd", [:mix, :rebar3], [], "hexpm", "72f3840fedd94f06315c523f6cecf5b4827233bed7ae3fe135b2a0ebeab5e196"},
"remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "825dc00aaba5a1b7c4202a532b696b595dd3bcb3", [ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"]}, "remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]},
"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"},

View file

@ -3,7 +3,6 @@ defmodule Pleroma.Repo.Migrations.MigrateOldBookmarks do
import Ecto.Query import Ecto.Query
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Bookmark alias Pleroma.Bookmark
alias Pleroma.User
alias Pleroma.Repo alias Pleroma.Repo
def up do def up do

View file

@ -1,6 +1,5 @@
defmodule Pleroma.Repo.Migrations.CreateSafeJsonbSet do defmodule Pleroma.Repo.Migrations.CreateSafeJsonbSet do
use Ecto.Migration use Ecto.Migration
alias Pleroma.User
def change do def change do
execute(""" execute("""

View file

@ -0,0 +1,9 @@
defmodule Pleroma.Repo.Migrations.AddTrustedToApps do
use Ecto.Migration
def change do
alter table(:apps) do
add(:trusted, :boolean, default: false)
end
end
end

View file

@ -0,0 +1,29 @@
defmodule Pleroma.Repo.Migrations.ChangeFollowingRelationshipsStateToInteger do
use Ecto.Migration
@alter_following_relationship_state "ALTER TABLE following_relationships ALTER COLUMN state"
def up do
execute("""
#{@alter_following_relationship_state} TYPE integer USING
CASE
WHEN state = 'pending' THEN 1
WHEN state = 'accept' THEN 2
WHEN state = 'reject' THEN 3
ELSE 0
END;
""")
end
def down do
execute("""
#{@alter_following_relationship_state} TYPE varchar(255) USING
CASE
WHEN state = 1 THEN 'pending'
WHEN state = 2 THEN 'accept'
WHEN state = 3 THEN 'reject'
ELSE ''
END;
""")
end
end

View file

@ -0,0 +1,11 @@
defmodule Pleroma.Repo.Migrations.AddFollowingRelationshipsFollowingIdIndex do
use Ecto.Migration
# [:follower_index] index is useless because of [:follower_id, :following_id] index
# [:following_id] index makes sense because of user's followers-targeted queries
def change do
drop_if_exists(index(:following_relationships, [:follower_id]))
create_if_not_exists(index(:following_relationships, [:following_id]))
end
end

View file

@ -0,0 +1,17 @@
defmodule Pleroma.Repo.Migrations.UsersAddPublicKey do
use Ecto.Migration
def up do
alter table(:users) do
add_if_not_exists(:public_key, :text)
end
execute("UPDATE users SET public_key = source_data->'publicKey'->>'publicKeyPem'")
end
def down do
alter table(:users) do
remove_if_exists(:public_key, :text)
end
end
end

View file

@ -0,0 +1,20 @@
defmodule Pleroma.Repo.Migrations.UsersAddInboxes do
use Ecto.Migration
def up do
alter table(:users) do
add_if_not_exists(:inbox, :text)
add_if_not_exists(:shared_inbox, :text)
end
execute("UPDATE users SET inbox = source_data->>'inbox'")
execute("UPDATE users SET shared_inbox = source_data->'endpoints'->>'sharedInbox'")
end
def down do
alter table(:users) do
remove_if_exists(:inbox, :text)
remove_if_exists(:shared_inbox, :text)
end
end
end

View file

@ -0,0 +1,11 @@
defmodule Pleroma.Repo.Migrations.UpdateObanJobsTable do
use Ecto.Migration
def up do
Oban.Migrations.up(version: 8)
end
def down do
Oban.Migrations.down(version: 7)
end
end

View file

@ -0,0 +1,35 @@
defmodule Pleroma.Repo.Migrations.UsersPopulateEmoji do
use Ecto.Migration
import Ecto.Query
alias Pleroma.User
alias Pleroma.Repo
def up do
execute("ALTER TABLE users ALTER COLUMN emoji SET DEFAULT '{}'::jsonb")
execute("UPDATE users SET emoji = DEFAULT WHERE emoji = '[]'::jsonb")
from(u in User)
|> select([u], struct(u, [:id, :ap_id, :source_data]))
|> Repo.stream()
|> Enum.each(fn user ->
emoji =
user.source_data
|> Map.get("tag", [])
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc ->
Map.put(acc, String.trim(name, ":"), url)
end)
user
|> Ecto.Changeset.cast(%{emoji: emoji}, [:emoji])
|> Repo.update()
end)
end
def down do
execute("ALTER TABLE users ALTER COLUMN emoji SET DEFAULT '[]'::jsonb")
execute("UPDATE users SET emoji = DEFAULT WHERE emoji = '{}'::jsonb")
end
end

View file

@ -0,0 +1,15 @@
defmodule Pleroma.Repo.Migrations.UsersRemoveSourceData do
use Ecto.Migration
def up do
alter table(:users) do
remove_if_exists(:source_data, :map)
end
end
def down do
alter table(:users) do
add_if_not_exists(:source_data, :map, default: %{})
end
end
end

View file

@ -16,6 +16,7 @@ defmodule Pleroma.Config.TransferTaskTest do
refute Application.get_env(:pleroma, :test_key) refute Application.get_env(:pleroma, :test_key)
refute Application.get_env(:idna, :test_key) refute Application.get_env(:idna, :test_key)
refute Application.get_env(:quack, :test_key) refute Application.get_env(:quack, :test_key)
initial = Application.get_env(:logger, :level)
ConfigDB.create(%{ ConfigDB.create(%{
group: ":pleroma", group: ":pleroma",
@ -35,16 +36,20 @@ defmodule Pleroma.Config.TransferTaskTest do
value: [:test_value1, :test_value2] value: [:test_value1, :test_value2]
}) })
ConfigDB.create(%{group: ":logger", key: ":level", value: :debug})
TransferTask.start_link([]) TransferTask.start_link([])
assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3] assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3]
assert Application.get_env(:idna, :test_key) == [live: 15, com: 35] assert Application.get_env(:idna, :test_key) == [live: 15, com: 35]
assert Application.get_env(:quack, :test_key) == [:test_value1, :test_value2] assert Application.get_env(:quack, :test_key) == [:test_value1, :test_value2]
assert Application.get_env(:logger, :level) == :debug
on_exit(fn -> on_exit(fn ->
Application.delete_env(:pleroma, :test_key) Application.delete_env(:pleroma, :test_key)
Application.delete_env(:idna, :test_key) Application.delete_env(:idna, :test_key)
Application.delete_env(:quack, :test_key) Application.delete_env(:quack, :test_key)
Application.put_env(:logger, :level, initial)
end) end)
end end
@ -78,8 +83,8 @@ defmodule Pleroma.Config.TransferTaskTest do
end end
test "transfer config values with full subkey update" do test "transfer config values with full subkey update" do
emoji = Application.get_env(:pleroma, :emoji) clear_config(:emoji)
assets = Application.get_env(:pleroma, :assets) clear_config(:assets)
ConfigDB.create(%{ ConfigDB.create(%{
group: ":pleroma", group: ":pleroma",
@ -99,11 +104,6 @@ defmodule Pleroma.Config.TransferTaskTest do
assert emoji_env[:groups] == [a: 1, b: 2] assert emoji_env[:groups] == [a: 1, b: 2]
assets_env = Application.get_env(:pleroma, :assets) assets_env = Application.get_env(:pleroma, :assets)
assert assets_env[:mascots] == [a: 1, b: 2] assert assets_env[:mascots] == [a: 1, b: 2]
on_exit(fn ->
Application.put_env(:pleroma, :emoji, emoji)
Application.put_env(:pleroma, :assets, assets)
end)
end end
describe "pleroma restart" do describe "pleroma restart" do
@ -112,8 +112,7 @@ defmodule Pleroma.Config.TransferTaskTest do
end end
test "don't restart if no reboot time settings were changed" do test "don't restart if no reboot time settings were changed" do
emoji = Application.get_env(:pleroma, :emoji) clear_config(:emoji)
on_exit(fn -> Application.put_env(:pleroma, :emoji, emoji) end)
ConfigDB.create(%{ ConfigDB.create(%{
group: ":pleroma", group: ":pleroma",
@ -128,8 +127,7 @@ defmodule Pleroma.Config.TransferTaskTest do
end end
test "on reboot time key" do test "on reboot time key" do
chat = Application.get_env(:pleroma, :chat) clear_config(:chat)
on_exit(fn -> Application.put_env(:pleroma, :chat, chat) end)
ConfigDB.create(%{ ConfigDB.create(%{
group: ":pleroma", group: ":pleroma",
@ -141,8 +139,7 @@ defmodule Pleroma.Config.TransferTaskTest do
end end
test "on reboot time subkey" do test "on reboot time subkey" do
captcha = Application.get_env(:pleroma, Pleroma.Captcha) clear_config(Pleroma.Captcha)
on_exit(fn -> Application.put_env(:pleroma, Pleroma.Captcha, captcha) end)
ConfigDB.create(%{ ConfigDB.create(%{
group: ":pleroma", group: ":pleroma",
@ -154,13 +151,8 @@ defmodule Pleroma.Config.TransferTaskTest do
end end
test "don't restart pleroma on reboot time key and subkey if there is false flag" do test "don't restart pleroma on reboot time key and subkey if there is false flag" do
chat = Application.get_env(:pleroma, :chat) clear_config(:chat)
captcha = Application.get_env(:pleroma, Pleroma.Captcha) clear_config(Pleroma.Captcha)
on_exit(fn ->
Application.put_env(:pleroma, :chat, chat)
Application.put_env(:pleroma, Pleroma.Captcha, captcha)
end)
ConfigDB.create(%{ ConfigDB.create(%{
group: ":pleroma", group: ":pleroma",

View file

@ -3,7 +3,6 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emoji.FormatterTest do defmodule Pleroma.Emoji.FormatterTest do
alias Pleroma.Emoji
alias Pleroma.Emoji.Formatter alias Pleroma.Emoji.Formatter
use Pleroma.DataCase use Pleroma.DataCase
@ -32,30 +31,19 @@ defmodule Pleroma.Emoji.FormatterTest do
end end
end end
describe "get_emoji" do describe "get_emoji_map" do
test "it returns the emoji used in the text" do test "it returns the emoji used in the text" do
text = "I love :firefox:" assert Formatter.get_emoji_map("I love :firefox:") == %{
"firefox" => "http://localhost:4001/emoji/Firefox.gif"
assert Formatter.get_emoji(text) == [ }
{"firefox",
%Emoji{
code: "firefox",
file: "/emoji/Firefox.gif",
tags: ["Gif", "Fun"],
safe_code: "firefox",
safe_file: "/emoji/Firefox.gif"
}}
]
end end
test "it returns a nice empty result when no emojis are present" do test "it returns a nice empty result when no emojis are present" do
text = "I love moominamma" assert Formatter.get_emoji_map("I love moominamma") == %{}
assert Formatter.get_emoji(text) == []
end end
test "it doesn't die when text is absent" do test "it doesn't die when text is absent" do
text = nil assert Formatter.get_emoji_map(nil) == %{}
assert Formatter.get_emoji(text) == []
end end
end end
end end

View file

@ -15,28 +15,28 @@ defmodule Pleroma.FollowingRelationshipTest do
test "returns following addresses without internal.fetch" do test "returns following addresses without internal.fetch" do
user = insert(:user) user = insert(:user)
fetch_actor = InternalFetchActor.get_actor() fetch_actor = InternalFetchActor.get_actor()
FollowingRelationship.follow(fetch_actor, user, "accept") FollowingRelationship.follow(fetch_actor, user, :follow_accept)
assert FollowingRelationship.following(fetch_actor) == [user.follower_address] assert FollowingRelationship.following(fetch_actor) == [user.follower_address]
end end
test "returns following addresses without relay" do test "returns following addresses without relay" do
user = insert(:user) user = insert(:user)
relay_actor = Relay.get_actor() relay_actor = Relay.get_actor()
FollowingRelationship.follow(relay_actor, user, "accept") FollowingRelationship.follow(relay_actor, user, :follow_accept)
assert FollowingRelationship.following(relay_actor) == [user.follower_address] assert FollowingRelationship.following(relay_actor) == [user.follower_address]
end end
test "returns following addresses without remote user" do test "returns following addresses without remote user" do
user = insert(:user) user = insert(:user)
actor = insert(:user, local: false) actor = insert(:user, local: false)
FollowingRelationship.follow(actor, user, "accept") FollowingRelationship.follow(actor, user, :follow_accept)
assert FollowingRelationship.following(actor) == [user.follower_address] assert FollowingRelationship.following(actor) == [user.follower_address]
end end
test "returns following addresses with local user" do test "returns following addresses with local user" do
user = insert(:user) user = insert(:user)
actor = insert(:user, local: true) actor = insert(:user, local: true)
FollowingRelationship.follow(actor, user, "accept") FollowingRelationship.follow(actor, user, :follow_accept)
assert FollowingRelationship.following(actor) == [ assert FollowingRelationship.following(actor) == [
actor.follower_address, actor.follower_address,

View file

@ -140,7 +140,7 @@ defmodule Pleroma.FormatterTest do
archaeme = archaeme =
insert(:user, insert(:user,
nickname: "archa_eme_", nickname: "archa_eme_",
source_data: %{"url" => "https://archeme/@archa_eme_"} uri: "https://archeme/@archa_eme_"
) )
archaeme_remote = insert(:user, %{nickname: "archaeme@archae.me"}) archaeme_remote = insert(:user, %{nickname: "archaeme@archae.me"})
@ -150,13 +150,13 @@ defmodule Pleroma.FormatterTest do
assert length(mentions) == 3 assert length(mentions) == 3
expected_text = expected_text =
~s(<span class="h-card"><a data-user="#{gsimg.id}" class="u-url mention" href="#{ ~s(<span class="h-card"><a class="u-url mention" data-user="#{gsimg.id}" href="#{
gsimg.ap_id gsimg.ap_id
}" rel="ugc">@<span>gsimg</span></a></span> According to <span class="h-card"><a data-user="#{ }" rel="ugc">@<span>gsimg</span></a></span> According to <span class="h-card"><a class="u-url mention" data-user="#{
archaeme.id archaeme.id
}" class="u-url mention" href="#{"https://archeme/@archa_eme_"}" rel="ugc">@<span>archa_eme_</span></a></span>, that is @daggsy. Also hello <span class="h-card"><a data-user="#{ }" href="#{"https://archeme/@archa_eme_"}" rel="ugc">@<span>archa_eme_</span></a></span>, that is @daggsy. Also hello <span class="h-card"><a class="u-url mention" data-user="#{
archaeme_remote.id archaeme_remote.id
}" class="u-url mention" href="#{archaeme_remote.ap_id}" rel="ugc">@<span>archaeme</span></a></span>) }" href="#{archaeme_remote.ap_id}" rel="ugc">@<span>archaeme</span></a></span>)
assert expected_text == text assert expected_text == text
end end
@ -171,7 +171,7 @@ defmodule Pleroma.FormatterTest do
assert length(mentions) == 1 assert length(mentions) == 1
expected_text = expected_text =
~s(<span class="h-card"><a data-user="#{mike.id}" class="u-url mention" href="#{ ~s(<span class="h-card"><a class="u-url mention" data-user="#{mike.id}" href="#{
mike.ap_id mike.ap_id
}" rel="ugc">@<span>mike</span></a></span> test) }" rel="ugc">@<span>mike</span></a></span> test)
@ -187,7 +187,7 @@ defmodule Pleroma.FormatterTest do
assert length(mentions) == 1 assert length(mentions) == 1
expected_text = expected_text =
~s(<span class="h-card"><a data-user="#{o.id}" class="u-url mention" href="#{o.ap_id}" rel="ugc">@<span>o</span></a></span> hi) ~s(<span class="h-card"><a class="u-url mention" data-user="#{o.id}" href="#{o.ap_id}" rel="ugc">@<span>o</span></a></span> hi)
assert expected_text == text assert expected_text == text
end end
@ -209,17 +209,13 @@ defmodule Pleroma.FormatterTest do
assert mentions == [{"@#{user.nickname}", user}, {"@#{other_user.nickname}", other_user}] assert mentions == [{"@#{user.nickname}", user}, {"@#{other_user.nickname}", other_user}]
assert expected_text == assert expected_text ==
~s(<span class="h-card"><a data-user="#{user.id}" class="u-url mention" href="#{ ~s(<span class="h-card"><a class="u-url mention" data-user="#{user.id}" href="#{
user.ap_id user.ap_id
}" rel="ugc">@<span>#{user.nickname}</span></a></span> <span class="h-card"><a data-user="#{ }" rel="ugc">@<span>#{user.nickname}</span></a></span> <span class="h-card"><a class="u-url mention" data-user="#{
other_user.id other_user.id
}" class="u-url mention" href="#{other_user.ap_id}" rel="ugc">@<span>#{ }" href="#{other_user.ap_id}" rel="ugc">@<span>#{other_user.nickname}</span></a></span> hey dudes i hate <span class="h-card"><a class="u-url mention" data-user="#{
other_user.nickname
}</span></a></span> hey dudes i hate <span class="h-card"><a data-user="#{
third_user.id third_user.id
}" class="u-url mention" href="#{third_user.ap_id}" rel="ugc">@<span>#{ }" href="#{third_user.ap_id}" rel="ugc">@<span>#{third_user.nickname}</span></a></span>)
third_user.nickname
}</span></a></span>)
end end
test "given the 'safe_mention' option, it will still work without any mention" do test "given the 'safe_mention' option, it will still work without any mention" do

View file

@ -7,6 +7,7 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Plugs.PlugHelper
alias Pleroma.Repo alias Pleroma.Repo
import Mock import Mock
@ -16,6 +17,18 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do
:ok :ok
end end
test "is not performed if marked as skipped", %{conn: conn} do
with_mock OAuthScopesPlug, [:passthrough], perform: &passthrough([&1, &2]) do
conn =
conn
|> PlugHelper.append_to_skipped_plugs(OAuthScopesPlug)
|> OAuthScopesPlug.call(%{scopes: ["random_scope"]})
refute called(OAuthScopesPlug.perform(:_, :_))
refute conn.halted
end
end
test "if `token.scopes` fulfills specified 'any of' conditions, " <> test "if `token.scopes` fulfills specified 'any of' conditions, " <>
"proceeds with no op", "proceeds with no op",
%{conn: conn} do %{conn: conn} do

View file

@ -5,8 +5,10 @@
defmodule Pleroma.Plugs.RateLimiterTest do defmodule Pleroma.Plugs.RateLimiterTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
alias Phoenix.ConnTest
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Plugs.RateLimiter alias Pleroma.Plugs.RateLimiter
alias Plug.Conn
import Pleroma.Factory import Pleroma.Factory
import Pleroma.Tests.Helpers, only: [clear_config: 1, clear_config: 2] import Pleroma.Tests.Helpers, only: [clear_config: 1, clear_config: 2]
@ -36,8 +38,15 @@ defmodule Pleroma.Plugs.RateLimiterTest do
end end
test "it is disabled if it remote ip plug is enabled but no remote ip is found" do test "it is disabled if it remote ip plug is enabled but no remote ip is found" do
Config.put([Pleroma.Web.Endpoint, :http, :ip], {127, 0, 0, 1}) assert RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, false))
assert RateLimiter.disabled?(Plug.Conn.assign(build_conn(), :remote_ip_found, false)) end
test "it is enabled if remote ip found" do
refute RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, true))
end
test "it is enabled if remote_ip_found flag doesn't exist" do
refute RateLimiter.disabled?(build_conn())
end end
test "it restricts based on config values" do test "it restricts based on config values" do
@ -58,7 +67,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
end end
conn = RateLimiter.call(conn, plug_opts) conn = RateLimiter.call(conn, plug_opts)
assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
assert conn.halted assert conn.halted
Process.sleep(50) Process.sleep(50)
@ -68,7 +77,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
conn = RateLimiter.call(conn, plug_opts) conn = RateLimiter.call(conn, plug_opts)
assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
refute conn.status == Plug.Conn.Status.code(:too_many_requests) refute conn.status == Conn.Status.code(:too_many_requests)
refute conn.resp_body refute conn.resp_body
refute conn.halted refute conn.halted
end end
@ -98,7 +107,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
plug_opts = RateLimiter.init(name: limiter_name, params: ["id"]) plug_opts = RateLimiter.init(name: limiter_name, params: ["id"])
conn = build_conn(:get, "/?id=1") conn = build_conn(:get, "/?id=1")
conn = Plug.Conn.fetch_query_params(conn) conn = Conn.fetch_query_params(conn)
conn_2 = build_conn(:get, "/?id=2") conn_2 = build_conn(:get, "/?id=2")
RateLimiter.call(conn, plug_opts) RateLimiter.call(conn, plug_opts)
@ -119,7 +128,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
id = "100" id = "100"
conn = build_conn(:get, "/?id=#{id}") conn = build_conn(:get, "/?id=#{id}")
conn = Plug.Conn.fetch_query_params(conn) conn = Conn.fetch_query_params(conn)
conn_2 = build_conn(:get, "/?id=#{101}") conn_2 = build_conn(:get, "/?id=#{101}")
RateLimiter.call(conn, plug_opts) RateLimiter.call(conn, plug_opts)
@ -147,13 +156,13 @@ defmodule Pleroma.Plugs.RateLimiterTest do
conn = RateLimiter.call(conn, plug_opts) conn = RateLimiter.call(conn, plug_opts)
assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
assert conn.halted assert conn.halted
conn_2 = RateLimiter.call(conn_2, plug_opts) conn_2 = RateLimiter.call(conn_2, plug_opts)
assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts) assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts)
refute conn_2.status == Plug.Conn.Status.code(:too_many_requests) refute conn_2.status == Conn.Status.code(:too_many_requests)
refute conn_2.resp_body refute conn_2.resp_body
refute conn_2.halted refute conn_2.halted
end end
@ -187,7 +196,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
conn = RateLimiter.call(conn, plug_opts) conn = RateLimiter.call(conn, plug_opts)
assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
assert conn.halted assert conn.halted
end end
@ -210,12 +219,12 @@ defmodule Pleroma.Plugs.RateLimiterTest do
end end
conn = RateLimiter.call(conn, plug_opts) conn = RateLimiter.call(conn, plug_opts)
assert %{"error" => "Throttled"} = Phoenix.ConnTest.json_response(conn, :too_many_requests) assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
assert conn.halted assert conn.halted
conn_2 = RateLimiter.call(conn_2, plug_opts) conn_2 = RateLimiter.call(conn_2, plug_opts)
assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts) assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts)
refute conn_2.status == Plug.Conn.Status.code(:too_many_requests) refute conn_2.status == Conn.Status.code(:too_many_requests)
refute conn_2.resp_body refute conn_2.resp_body
refute conn_2.halted refute conn_2.halted
end end

View file

@ -19,12 +19,7 @@ defmodule Pleroma.SignatureTest do
@private_key "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA48qb4v6kqigZutO9Ot0wkp27GIF2LiVaADgxQORZozZR63jH\nTaoOrS3Xhngbgc8SSOhfXET3omzeCLqaLNfXnZ8OXmuhJfJSU6mPUvmZ9QdT332j\nfN/g3iWGhYMf/M9ftCKh96nvFVO/tMruzS9xx7tkrfJjehdxh/3LlJMMImPtwcD7\nkFXwyt1qZTAU6Si4oQAJxRDQXHp1ttLl3Ob829VM7IKkrVmY8TD+JSlV0jtVJPj6\n1J19ytKTx/7UaucYvb9HIiBpkuiy5n/irDqKLVf5QEdZoNCdojOZlKJmTLqHhzKP\n3E9TxsUjhrf4/EqegNc/j982RvOxeu4i40zMQwIDAQABAoIBAQDH5DXjfh21i7b4\ncXJuw0cqget617CDUhemdakTDs9yH+rHPZd3mbGDWuT0hVVuFe4vuGpmJ8c+61X0\nRvugOlBlavxK8xvYlsqTzAmPgKUPljyNtEzQ+gz0I+3mH2jkin2rL3D+SksZZgKm\nfiYMPIQWB2WUF04gB46DDb2mRVuymGHyBOQjIx3WC0KW2mzfoFUFRlZEF+Nt8Ilw\nT+g/u0aZ1IWoszbsVFOEdghgZET0HEarum0B2Je/ozcPYtwmU10iBANGMKdLqaP/\nj954BPunrUf6gmlnLZKIKklJj0advx0NA+cL79+zeVB3zexRYSA5o9q0WPhiuTwR\n/aedWHnBAoGBAP0sDWBAM1Y4TRAf8ZI9PcztwLyHPzfEIqzbObJJnx1icUMt7BWi\n+/RMOnhrlPGE1kMhOqSxvXYN3u+eSmWTqai2sSH5Hdw2EqnrISSTnwNUPINX7fHH\njEkgmXQ6ixE48SuBZnb4w1EjdB/BA6/sjL+FNhggOc87tizLTkMXmMtTAoGBAOZV\n+wPuAMBDBXmbmxCuDIjoVmgSlgeRunB1SA8RCPAFAiUo3+/zEgzW2Oz8kgI+xVwM\n33XkLKrWG1Orhpp6Hm57MjIc5MG+zF4/YRDpE/KNG9qU1tiz0UD5hOpIU9pP4bR/\ngxgPxZzvbk4h5BfHWLpjlk8UUpgk6uxqfti48c1RAoGBALBOKDZ6HwYRCSGMjUcg\n3NPEUi84JD8qmFc2B7Tv7h2he2ykIz9iFAGpwCIyETQsJKX1Ewi0OlNnD3RhEEAy\nl7jFGQ+mkzPSeCbadmcpYlgIJmf1KN/x7fDTAepeBpCEzfZVE80QKbxsaybd3Dp8\nCfwpwWUFtBxr4c7J+gNhAGe/AoGAPn8ZyqkrPv9wXtyfqFjxQbx4pWhVmNwrkBPi\nZ2Qh3q4dNOPwTvTO8vjghvzIyR8rAZzkjOJKVFgftgYWUZfM5gE7T2mTkBYq8W+U\n8LetF+S9qAM2gDnaDx0kuUTCq7t87DKk6URuQ/SbI0wCzYjjRD99KxvChVGPBHKo\n1DjqMuECgYEAgJGNm7/lJCS2wk81whfy/ttKGsEIkyhPFYQmdGzSYC5aDc2gp1R3\nxtOkYEvdjfaLfDGEa4UX8CHHF+w3t9u8hBtcdhMH6GYb9iv6z0VBTt4A/11HUR49\n3Z7TQ18Iyh3jAUCzFV9IJlLIExq5Y7P4B3ojWFBN607sDCt8BMPbDYs=\n-----END RSA PRIVATE KEY-----" @private_key "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA48qb4v6kqigZutO9Ot0wkp27GIF2LiVaADgxQORZozZR63jH\nTaoOrS3Xhngbgc8SSOhfXET3omzeCLqaLNfXnZ8OXmuhJfJSU6mPUvmZ9QdT332j\nfN/g3iWGhYMf/M9ftCKh96nvFVO/tMruzS9xx7tkrfJjehdxh/3LlJMMImPtwcD7\nkFXwyt1qZTAU6Si4oQAJxRDQXHp1ttLl3Ob829VM7IKkrVmY8TD+JSlV0jtVJPj6\n1J19ytKTx/7UaucYvb9HIiBpkuiy5n/irDqKLVf5QEdZoNCdojOZlKJmTLqHhzKP\n3E9TxsUjhrf4/EqegNc/j982RvOxeu4i40zMQwIDAQABAoIBAQDH5DXjfh21i7b4\ncXJuw0cqget617CDUhemdakTDs9yH+rHPZd3mbGDWuT0hVVuFe4vuGpmJ8c+61X0\nRvugOlBlavxK8xvYlsqTzAmPgKUPljyNtEzQ+gz0I+3mH2jkin2rL3D+SksZZgKm\nfiYMPIQWB2WUF04gB46DDb2mRVuymGHyBOQjIx3WC0KW2mzfoFUFRlZEF+Nt8Ilw\nT+g/u0aZ1IWoszbsVFOEdghgZET0HEarum0B2Je/ozcPYtwmU10iBANGMKdLqaP/\nj954BPunrUf6gmlnLZKIKklJj0advx0NA+cL79+zeVB3zexRYSA5o9q0WPhiuTwR\n/aedWHnBAoGBAP0sDWBAM1Y4TRAf8ZI9PcztwLyHPzfEIqzbObJJnx1icUMt7BWi\n+/RMOnhrlPGE1kMhOqSxvXYN3u+eSmWTqai2sSH5Hdw2EqnrISSTnwNUPINX7fHH\njEkgmXQ6ixE48SuBZnb4w1EjdB/BA6/sjL+FNhggOc87tizLTkMXmMtTAoGBAOZV\n+wPuAMBDBXmbmxCuDIjoVmgSlgeRunB1SA8RCPAFAiUo3+/zEgzW2Oz8kgI+xVwM\n33XkLKrWG1Orhpp6Hm57MjIc5MG+zF4/YRDpE/KNG9qU1tiz0UD5hOpIU9pP4bR/\ngxgPxZzvbk4h5BfHWLpjlk8UUpgk6uxqfti48c1RAoGBALBOKDZ6HwYRCSGMjUcg\n3NPEUi84JD8qmFc2B7Tv7h2he2ykIz9iFAGpwCIyETQsJKX1Ewi0OlNnD3RhEEAy\nl7jFGQ+mkzPSeCbadmcpYlgIJmf1KN/x7fDTAepeBpCEzfZVE80QKbxsaybd3Dp8\nCfwpwWUFtBxr4c7J+gNhAGe/AoGAPn8ZyqkrPv9wXtyfqFjxQbx4pWhVmNwrkBPi\nZ2Qh3q4dNOPwTvTO8vjghvzIyR8rAZzkjOJKVFgftgYWUZfM5gE7T2mTkBYq8W+U\n8LetF+S9qAM2gDnaDx0kuUTCq7t87DKk6URuQ/SbI0wCzYjjRD99KxvChVGPBHKo\n1DjqMuECgYEAgJGNm7/lJCS2wk81whfy/ttKGsEIkyhPFYQmdGzSYC5aDc2gp1R3\nxtOkYEvdjfaLfDGEa4UX8CHHF+w3t9u8hBtcdhMH6GYb9iv6z0VBTt4A/11HUR49\n3Z7TQ18Iyh3jAUCzFV9IJlLIExq5Y7P4B3ojWFBN607sDCt8BMPbDYs=\n-----END RSA PRIVATE KEY-----"
@public_key %{ @public_key "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0P/Tq4gb4G/QVuMGbJo\nC/AfMNcv+m7NfrlOwkVzcU47jgESuYI4UtJayissCdBycHUnfVUd9qol+eznSODz\nCJhfJloqEIC+aSnuEPGA0POtWad6DU0E6/Ho5zQn5WAWUwbRQqowbrsm/GHo2+3v\neR5jGenwA6sYhINg/c3QQbksyV0uJ20Umyx88w8+TJuv53twOfmyDWuYNoQ3y5cc\nHKOZcLHxYOhvwg3PFaGfFHMFiNmF40dTXt9K96r7sbzc44iLD+VphbMPJEjkMuf8\nPGEFOBzy8pm3wJZw2v32RNW2VESwMYyqDzwHXGSq1a73cS7hEnc79gXlELsK04L9\nQQIDAQAB\n-----END PUBLIC KEY-----\n"
"id" => "https://mastodon.social/users/lambadalambda#main-key",
"owner" => "https://mastodon.social/users/lambadalambda",
"publicKeyPem" =>
"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0P/Tq4gb4G/QVuMGbJo\nC/AfMNcv+m7NfrlOwkVzcU47jgESuYI4UtJayissCdBycHUnfVUd9qol+eznSODz\nCJhfJloqEIC+aSnuEPGA0POtWad6DU0E6/Ho5zQn5WAWUwbRQqowbrsm/GHo2+3v\neR5jGenwA6sYhINg/c3QQbksyV0uJ20Umyx88w8+TJuv53twOfmyDWuYNoQ3y5cc\nHKOZcLHxYOhvwg3PFaGfFHMFiNmF40dTXt9K96r7sbzc44iLD+VphbMPJEjkMuf8\nPGEFOBzy8pm3wJZw2v32RNW2VESwMYyqDzwHXGSq1a73cS7hEnc79gXlELsK04L9\nQQIDAQAB\n-----END PUBLIC KEY-----\n"
}
@rsa_public_key { @rsa_public_key {
:RSAPublicKey, :RSAPublicKey,
@ -42,7 +37,7 @@ defmodule Pleroma.SignatureTest do
test "it returns key" do test "it returns key" do
expected_result = {:ok, @rsa_public_key} expected_result = {:ok, @rsa_public_key}
user = insert(:user, source_data: %{"publicKey" => @public_key}) user = insert(:user, public_key: @public_key)
assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == expected_result assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == expected_result
end end
@ -53,8 +48,8 @@ defmodule Pleroma.SignatureTest do
end) =~ "[error] Could not decode user" end) =~ "[error] Could not decode user"
end end
test "it returns error if public key is empty" do test "it returns error if public key is nil" do
user = insert(:user, source_data: %{"publicKey" => %{}}) user = insert(:user, public_key: nil)
assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == {:error, :error} assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == {:error, :error}
end end

View file

@ -294,7 +294,7 @@ defmodule Pleroma.Factory do
def oauth_app_factory do def oauth_app_factory do
%Pleroma.Web.OAuth.App{ %Pleroma.Web.OAuth.App{
client_name: "Some client", client_name: sequence(:client_name, &"Some client #{&1}"),
redirect_uris: "https://example.com/callback", redirect_uris: "https://example.com/callback",
scopes: ["read", "write", "follow", "push", "admin"], scopes: ["read", "write", "follow", "push", "admin"],
website: "https://example.com", website: "https://example.com",

Some files were not shown because too many files have changed in this diff Show more