Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms
This commit is contained in:
commit
8c2c325598
|
@ -12,10 +12,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- 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.
|
||||
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required.
|
||||
- Mix task to create trusted OAuth App.
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
- 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>
|
||||
|
||||
### Fixed
|
||||
|
@ -25,6 +27,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
### 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
|
||||
|
|
|
@ -786,6 +786,8 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
|||
|
||||
### Restarts pleroma application
|
||||
|
||||
**Only works when configuration from database is enabled.**
|
||||
|
||||
- Params: none
|
||||
- Response:
|
||||
- On failure:
|
||||
|
@ -795,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 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.**
|
||||
|
||||
|
@ -821,13 +836,12 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
|||
"need_reboot": true
|
||||
}
|
||||
```
|
||||
need_reboot - *optional*, if were changed reboot time settings.
|
||||
|
||||
## `POST /api/pleroma/admin/config`
|
||||
|
||||
### 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.**
|
||||
|
||||
|
@ -971,7 +985,6 @@ config :quack,
|
|||
"need_reboot": true
|
||||
}
|
||||
```
|
||||
need_reboot - *optional*, if were changed reboot time settings.
|
||||
|
||||
## ` GET /api/pleroma/admin/config/descriptions`
|
||||
|
||||
|
@ -1075,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
|
|
@ -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
|
||||
```
|
|
@ -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
|
|
@ -122,7 +122,7 @@ defp configure({_, :backends, _, merged}) do
|
|||
:ok = update_env(:logger, :backends, merged)
|
||||
end
|
||||
|
||||
defp configure({group, key, _, merged}) do
|
||||
defp configure({_, key, _, merged}) when key in [:console, :ex_syslogger] do
|
||||
merged =
|
||||
if key == :console do
|
||||
put_in(merged[:format], merged[:format] <> "\n")
|
||||
|
@ -136,7 +136,12 @@ defp configure({group, key, _, merged}) do
|
|||
else: key
|
||||
|
||||
Logger.configure_backend(backend, merged)
|
||||
:ok = update_env(:logger, group, 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
|
||||
|
|
|
@ -38,22 +38,14 @@ def demojify(text) do
|
|||
|
||||
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"
|
||||
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 ->
|
||||
Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}")
|
||||
end)
|
||||
end
|
||||
|
||||
def get_emoji_map(_), do: []
|
||||
def get_emoji_map(_), do: %{}
|
||||
end
|
||||
|
|
|
@ -31,7 +31,7 @@ def escape_mention_handler("@" <> nickname = mention, buffer, _, _) do
|
|||
def mention_handler("@" <> nickname, buffer, opts, acc) do
|
||||
case User.get_cached_by_nickname(nickname) do
|
||||
%User{id: id} = user ->
|
||||
ap_id = get_ap_id(user)
|
||||
user_url = user.uri || user.ap_id
|
||||
nickname_text = get_nickname_text(nickname, opts)
|
||||
|
||||
link =
|
||||
|
@ -42,7 +42,7 @@ def mention_handler("@" <> nickname, buffer, opts, acc) do
|
|||
["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)],
|
||||
"data-user": id,
|
||||
class: "u-url mention",
|
||||
href: ap_id,
|
||||
href: user_url,
|
||||
rel: "ugc"
|
||||
),
|
||||
class: "h-card"
|
||||
|
@ -146,9 +146,6 @@ def truncate(text, max_length \\ 200, omission \\ "...") do
|
|||
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, _), do: User.local_nickname(nickname)
|
||||
end
|
||||
|
|
|
@ -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
|
|
@ -8,12 +8,15 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
|
|||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Plugs.PlugHelper
|
||||
|
||||
use Pleroma.Web, :plug
|
||||
|
||||
@behaviour Plug
|
||||
|
||||
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] || :|
|
||||
token = assigns[:token]
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -15,6 +15,7 @@ defmodule Pleroma.User do
|
|||
alias Pleroma.Config
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Delivery
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.FollowingRelationship
|
||||
alias Pleroma.Formatter
|
||||
alias Pleroma.HTML
|
||||
|
@ -28,6 +29,7 @@ defmodule Pleroma.User do
|
|||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
||||
|
@ -82,6 +84,7 @@ defmodule Pleroma.User do
|
|||
field(:password, :string, virtual: true)
|
||||
field(:password_confirmation, :string, virtual: true)
|
||||
field(:keys, :string)
|
||||
field(:public_key, :string)
|
||||
field(:ap_id, :string)
|
||||
field(:avatar, :map)
|
||||
field(:local, :boolean, default: true)
|
||||
|
@ -94,7 +97,6 @@ defmodule Pleroma.User do
|
|||
field(:last_digest_emailed_at, :naive_datetime)
|
||||
field(:banner, :map, default: %{})
|
||||
field(:background, :map, default: %{})
|
||||
field(:source_data, :map, default: %{})
|
||||
field(:note_count, :integer, default: 0)
|
||||
field(:follower_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(:settings, :map, 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_follows_count, :boolean, default: false)
|
||||
field(:hide_followers, :boolean, default: false)
|
||||
|
@ -122,7 +124,7 @@ defmodule Pleroma.User do
|
|||
field(:pinned_activities, {:array, :string}, default: [])
|
||||
field(:email_notifications, :map, default: %{"digest" => false})
|
||||
field(:mascot, :map, default: nil)
|
||||
field(:emoji, {:array, :map}, default: [])
|
||||
field(:emoji, :map, default: %{})
|
||||
field(:pleroma_settings_store, :map, default: %{})
|
||||
field(: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(:actor_type, :string, default: "Person")
|
||||
field(:also_known_as, {:array, :string}, default: [])
|
||||
field(:inbox, :string)
|
||||
field(:shared_inbox, :string)
|
||||
|
||||
embeds_one(
|
||||
:notification_settings,
|
||||
|
@ -306,6 +310,7 @@ def banner_url(user, options \\ []) do
|
|||
end
|
||||
end
|
||||
|
||||
# Should probably be renamed or removed
|
||||
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
|
||||
|
@ -339,62 +344,72 @@ defp truncate_if_exists(params, key, max_length) do
|
|||
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)
|
||||
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
|
||||
|> Map.put(:name, name)
|
||||
|> Map.put_new(:last_refreshed_at, NaiveDateTime.utc_now())
|
||||
|> truncate_if_exists(:name, name_limit)
|
||||
|> truncate_if_exists(:bio, bio_limit)
|
||||
|> truncate_fields_param()
|
||||
|> fix_follower_address()
|
||||
|
||||
changeset =
|
||||
%User{local: false}
|
||||
|> cast(
|
||||
params,
|
||||
[
|
||||
:bio,
|
||||
:name,
|
||||
:ap_id,
|
||||
:nickname,
|
||||
:avatar,
|
||||
:ap_enabled,
|
||||
:source_data,
|
||||
:banner,
|
||||
:locked,
|
||||
:magic_key,
|
||||
:uri,
|
||||
:hide_followers,
|
||||
:hide_follows,
|
||||
:hide_followers_count,
|
||||
:hide_follows_count,
|
||||
:follower_count,
|
||||
:fields,
|
||||
:following_count,
|
||||
:discoverable,
|
||||
:invisible,
|
||||
:actor_type,
|
||||
:also_known_as
|
||||
]
|
||||
)
|
||||
|> validate_required([:name, :ap_id])
|
||||
|> unique_constraint(:nickname)
|
||||
|> validate_format(:nickname, @email_regex)
|
||||
|> validate_length(:bio, max: bio_limit)
|
||||
|> validate_length(:name, max: name_limit)
|
||||
|> validate_fields(true)
|
||||
|
||||
case params[:source_data] do
|
||||
%{"followers" => followers, "following" => following} ->
|
||||
changeset
|
||||
|> put_change(:follower_address, followers)
|
||||
|> put_change(:following_address, following)
|
||||
|
||||
_ ->
|
||||
followers = ap_followers(%User{nickname: get_field(changeset, :nickname)})
|
||||
put_change(changeset, :follower_address, followers)
|
||||
end
|
||||
struct
|
||||
|> cast(
|
||||
params,
|
||||
[
|
||||
:bio,
|
||||
:name,
|
||||
:emoji,
|
||||
:ap_id,
|
||||
:inbox,
|
||||
:shared_inbox,
|
||||
:nickname,
|
||||
:public_key,
|
||||
:avatar,
|
||||
:ap_enabled,
|
||||
:banner,
|
||||
:locked,
|
||||
:last_refreshed_at,
|
||||
:magic_key,
|
||||
:uri,
|
||||
:follower_address,
|
||||
:following_address,
|
||||
:hide_followers,
|
||||
:hide_follows,
|
||||
:hide_followers_count,
|
||||
:hide_follows_count,
|
||||
:follower_count,
|
||||
:fields,
|
||||
:following_count,
|
||||
:discoverable,
|
||||
:invisible,
|
||||
:actor_type,
|
||||
:also_known_as
|
||||
]
|
||||
)
|
||||
|> validate_required([:name, :ap_id])
|
||||
|> unique_constraint(:nickname)
|
||||
|> validate_format(:nickname, @email_regex)
|
||||
|> validate_length(:bio, max: bio_limit)
|
||||
|> validate_length(:name, max: name_limit)
|
||||
|> validate_fields(true)
|
||||
end
|
||||
|
||||
def update_changeset(struct, params \\ %{}) do
|
||||
|
@ -407,7 +422,11 @@ def update_changeset(struct, params \\ %{}) do
|
|||
[
|
||||
:bio,
|
||||
:name,
|
||||
:emoji,
|
||||
:avatar,
|
||||
:public_key,
|
||||
:inbox,
|
||||
:shared_inbox,
|
||||
:locked,
|
||||
:no_rich_text,
|
||||
:default_scope,
|
||||
|
@ -434,6 +453,7 @@ def update_changeset(struct, params \\ %{}) do
|
|||
|> validate_length(:bio, max: bio_limit)
|
||||
|> validate_length(:name, min: 1, max: name_limit)
|
||||
|> put_fields()
|
||||
|> put_emoji()
|
||||
|> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
|
||||
|> put_change_if_present(:avatar, &put_upload(&1, :avatar))
|
||||
|> put_change_if_present(:banner, &put_upload(&1, :banner))
|
||||
|
@ -469,6 +489,18 @@ defp parse_fields(value) do
|
|||
|> elem(0)
|
||||
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
|
||||
if value = get_change(changeset, map_field) do
|
||||
with {:ok, new_value} <- value_function.(value) do
|
||||
|
@ -488,49 +520,6 @@ defp put_upload(value, type) do
|
|||
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
|
||||
struct
|
||||
|> update_changeset(params)
|
||||
|
@ -606,7 +595,7 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
|
|||
|
||||
struct
|
||||
|> 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_confirmation(:password)
|
||||
|> unique_constraint(:email)
|
||||
|
@ -1621,8 +1610,7 @@ defp create_service_actor(uri, nickname) do
|
|||
|> set_cache()
|
||||
end
|
||||
|
||||
# AP style
|
||||
def public_key(%{source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pem}}}) do
|
||||
def public_key(%{public_key: public_key_pem}) when is_binary(public_key_pem) do
|
||||
key =
|
||||
public_key_pem
|
||||
|> :public_key.pem_decode()
|
||||
|
@ -1632,7 +1620,7 @@ def public_key(%{source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pe
|
|||
{:ok, key}
|
||||
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
|
||||
with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id),
|
||||
|
@ -1643,17 +1631,6 @@ def get_public_key_for_ap_id(ap_id) do
|
|||
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{ap_enabled: ap_enabled}), do: ap_enabled
|
||||
def ap_enabled?(_), do: false
|
||||
|
@ -1962,12 +1939,6 @@ def update_background(user, background) do
|
|||
|> update_and_set_cache()
|
||||
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
|
||||
%{
|
||||
admin: is_admin,
|
||||
|
@ -1975,21 +1946,6 @@ def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do
|
|||
}
|
||||
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
|
||||
limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
|
||||
limit = Pleroma.Config.get([:instance, limit_name], 0)
|
||||
|
@ -2177,9 +2133,7 @@ def sanitize_html(%User{} = user) do
|
|||
# - display name
|
||||
def sanitize_html(%User{} = user, filter) do
|
||||
fields =
|
||||
user
|
||||
|> User.fields()
|
||||
|> Enum.map(fn %{"name" => name, "value" => value} ->
|
||||
Enum.map(user.fields, fn %{"name" => name, "value" => value} ->
|
||||
%{
|
||||
"name" => name,
|
||||
"value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
|
||||
|
|
|
@ -1441,19 +1441,41 @@ defp object_to_user_data(data) do
|
|||
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
|
||||
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
|
||||
|
||||
emojis =
|
||||
data
|
||||
|> Map.get("tag", [])
|
||||
|> Enum.filter(fn data -> data["type"] == "Emoji" and data["icon"] end)
|
||||
|> Enum.reduce(%{}, fn %{"icon" => %{"url" => url}, "name" => name}, acc ->
|
||||
Map.put(acc, String.trim(name, ":"), url)
|
||||
end)
|
||||
|
||||
locked = data["manuallyApprovesFollowers"] || false
|
||||
data = Transmogrifier.maybe_fix_user_object(data)
|
||||
discoverable = data["discoverable"] || false
|
||||
invisible = data["invisible"] || false
|
||||
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 = %{
|
||||
ap_id: data["id"],
|
||||
uri: get_actor_url(data["url"]),
|
||||
ap_enabled: true,
|
||||
source_data: data,
|
||||
banner: banner,
|
||||
fields: fields,
|
||||
emoji: emojis,
|
||||
locked: locked,
|
||||
discoverable: discoverable,
|
||||
invisible: invisible,
|
||||
|
@ -1463,7 +1485,10 @@ defp object_to_user_data(data) do
|
|||
following_address: data["following"],
|
||||
bio: data["summary"],
|
||||
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
|
||||
|
@ -1565,11 +1590,22 @@ def fetch_and_prepare_user_from_ap_id(ap_id) do
|
|||
end
|
||||
|
||||
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)
|
||||
else
|
||||
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
|
||||
e -> {:error, e}
|
||||
end
|
||||
|
|
|
@ -35,6 +35,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
|
|||
field(:like_count, :integer, default: 0)
|
||||
field(:announcement_count, :integer, default: 0)
|
||||
field(:inRepyTo, :string)
|
||||
field(:uri, Types.Uri)
|
||||
|
||||
field(:likes, {:array, :string}, default: [])
|
||||
field(:announcements, {:array, :string}, default: [])
|
||||
|
|
|
@ -15,15 +15,9 @@ def cast(object) when is_binary(object) do
|
|||
|
||||
def cast(%{"id" => object}), do: cast(object)
|
||||
|
||||
def cast(_) do
|
||||
:error
|
||||
end
|
||||
def cast(_), do: :error
|
||||
|
||||
def dump(data) do
|
||||
{:ok, data}
|
||||
end
|
||||
def dump(data), do: {:ok, data}
|
||||
|
||||
def load(data) do
|
||||
{:ok, data}
|
||||
end
|
||||
def load(data), do: {:ok, data}
|
||||
end
|
||||
|
|
|
@ -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
|
|
@ -141,8 +141,8 @@ defp get_cc_ap_ids(ap_id, recipients) do
|
|||
|> Enum.map(& &1.ap_id)
|
||||
end
|
||||
|
||||
defp maybe_use_sharedinbox(%User{source_data: data}),
|
||||
do: (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
|
||||
defp maybe_use_sharedinbox(%User{shared_inbox: nil, inbox: inbox}), do: inbox
|
||||
defp maybe_use_sharedinbox(%User{shared_inbox: shared_inbox}), do: shared_inbox
|
||||
|
||||
@doc """
|
||||
Determine a user inbox to use based on heuristics. These heuristics
|
||||
|
@ -157,7 +157,7 @@ defp maybe_use_sharedinbox(%User{source_data: data}),
|
|||
"""
|
||||
def determine_inbox(
|
||||
%Activity{data: activity_data},
|
||||
%User{source_data: data} = user
|
||||
%User{inbox: inbox} = user
|
||||
) do
|
||||
to = activity_data["to"] || []
|
||||
cc = activity_data["cc"] || []
|
||||
|
@ -174,7 +174,7 @@ def determine_inbox(
|
|||
maybe_use_sharedinbox(user)
|
||||
|
||||
true ->
|
||||
data["inbox"]
|
||||
inbox
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -192,14 +192,13 @@ def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity)
|
|||
inboxes =
|
||||
recipients
|
||||
|> 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)
|
||||
|> Instances.filter_reachable()
|
||||
|
||||
Repo.checkout(fn ->
|
||||
Enum.each(inboxes, fn {inbox, unreachable_since} ->
|
||||
%User{ap_id: ap_id} =
|
||||
Enum.find(recipients, fn %{source_data: data} -> data["inbox"] == inbox end)
|
||||
%User{ap_id: ap_id} = Enum.find(recipients, fn actor -> actor.inbox == inbox end)
|
||||
|
||||
# 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.
|
||||
|
|
|
@ -718,7 +718,7 @@ def handle_incoming(
|
|||
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
|
||||
|
||||
actor
|
||||
|> User.upgrade_changeset(new_user_data, true)
|
||||
|> User.remote_user_changeset(new_user_data)
|
||||
|> User.update_and_set_cache()
|
||||
|
||||
ActivityPub.update(%{
|
||||
|
@ -1167,7 +1167,7 @@ defp build_mention_tag(%{ap_id: ap_id, nickname: nickname} = _) do
|
|||
|
||||
def take_emoji_tags(%User{emoji: emoji}) do
|
||||
emoji
|
||||
|> Enum.flat_map(&Map.to_list/1)
|
||||
|> Map.to_list()
|
||||
|> Enum.map(&build_emoji_tag/1)
|
||||
end
|
||||
|
||||
|
@ -1261,12 +1261,8 @@ def perform(:user_upgrade, user) do
|
|||
def upgrade_user_from_ap_id(ap_id) do
|
||||
with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id),
|
||||
{:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id),
|
||||
already_ap <- User.ap_enabled?(user),
|
||||
{:ok, user} <- upgrade_user(user, data) do
|
||||
if not already_ap do
|
||||
TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id})
|
||||
end
|
||||
|
||||
{:ok, user} <- update_user(user, data) do
|
||||
TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id})
|
||||
{:ok, user}
|
||||
else
|
||||
%User{} = user -> {:ok, user}
|
||||
|
@ -1274,9 +1270,9 @@ def upgrade_user_from_ap_id(ap_id) do
|
|||
end
|
||||
end
|
||||
|
||||
defp upgrade_user(user, data) do
|
||||
defp update_user(user, data) do
|
||||
user
|
||||
|> User.upgrade_changeset(data, true)
|
||||
|> User.remote_user_changeset(data)
|
||||
|> User.update_and_set_cache()
|
||||
end
|
||||
|
||||
|
|
|
@ -79,10 +79,7 @@ def render("user.json", %{user: user}) do
|
|||
|
||||
emoji_tags = Transmogrifier.take_emoji_tags(user)
|
||||
|
||||
fields =
|
||||
user
|
||||
|> User.fields()
|
||||
|> Enum.map(&Map.put(&1, "type", "PropertyValue"))
|
||||
fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
|
||||
|
||||
%{
|
||||
"id" => user.ap_id,
|
||||
|
@ -103,7 +100,7 @@ def render("user.json", %{user: user}) do
|
|||
},
|
||||
"endpoints" => endpoints,
|
||||
"attachment" => fields,
|
||||
"tag" => (user.source_data["tag"] || []) ++ emoji_tags,
|
||||
"tag" => emoji_tags,
|
||||
"discoverable" => user.discoverable
|
||||
}
|
||||
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|
||||
|
|
|
@ -27,7 +27,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
alias Pleroma.Web.AdminAPI.Search
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.MastodonAPI.AppView
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.Router
|
||||
|
||||
require Logger
|
||||
|
@ -914,16 +916,7 @@ def config_show(conn, _params) do
|
|||
end)
|
||||
|> List.flatten()
|
||||
|
||||
response = %{configs: merged}
|
||||
|
||||
response =
|
||||
if Restarter.Pleroma.need_reboot?() do
|
||||
Map.put(response, :need_reboot, true)
|
||||
else
|
||||
response
|
||||
end
|
||||
|
||||
json(conn, response)
|
||||
json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -950,28 +943,22 @@ def config_update(conn, %{"configs" => configs}) do
|
|||
|
||||
Config.TransferTask.load_and_update_env(deleted, false)
|
||||
|
||||
need_reboot? =
|
||||
Restarter.Pleroma.need_reboot?() ||
|
||||
Enum.any?(updated, fn config ->
|
||||
if !Restarter.Pleroma.need_reboot?() do
|
||||
changed_reboot_settings? =
|
||||
(updated ++ deleted)
|
||||
|> Enum.any?(fn config ->
|
||||
group = ConfigDB.from_string(config.group)
|
||||
key = ConfigDB.from_string(config.key)
|
||||
value = ConfigDB.from_binary(config.value)
|
||||
Config.TransferTask.pleroma_need_restart?(group, key, value)
|
||||
end)
|
||||
|
||||
response = %{configs: updated}
|
||||
|
||||
response =
|
||||
if need_reboot? do
|
||||
Restarter.Pleroma.need_reboot()
|
||||
Map.put(response, :need_reboot, need_reboot?)
|
||||
else
|
||||
response
|
||||
end
|
||||
if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_view(ConfigView)
|
||||
|> render("index.json", response)
|
||||
|> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -983,6 +970,10 @@ def restart(conn, _params) do
|
|||
end
|
||||
end
|
||||
|
||||
def need_reboot(conn, _params) do
|
||||
json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
|
||||
end
|
||||
|
||||
defp configurable_from_database(conn) do
|
||||
if Config.get(:configurable_from_database) do
|
||||
:ok
|
||||
|
@ -1028,6 +1019,83 @@ def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" =
|
|||
conn |> json("")
|
||||
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
|
||||
count = Stats.get_status_visibility_count()
|
||||
|
||||
|
|
|
@ -361,26 +361,6 @@ defp maybe_create_activity_expiration({:ok, activity}, %NaiveDateTime{} = expire
|
|||
|
||||
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
|
||||
with %Activity{
|
||||
actor: ^user_ap_id,
|
||||
|
|
|
@ -10,7 +10,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.Formatter
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Plugs.AuthenticationPlug
|
||||
|
@ -18,7 +17,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
require Logger
|
||||
|
@ -175,7 +173,7 @@ def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_i
|
|||
"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_time =
|
||||
|
@ -431,19 +429,6 @@ def confirm_current_password(user, password) do
|
|||
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(
|
||||
recipients,
|
||||
%Activity{data: %{"to" => to, "type" => _type}} = _activity
|
||||
|
|
|
@ -23,7 +23,7 @@ def pub_date(date) when is_binary(date) do
|
|||
def pub_date(%DateTime{} = date), do: Timex.format!(date, "{RFC822}")
|
||||
|
||||
def prepare_activity(activity, opts \\ []) do
|
||||
object = activity_object(activity)
|
||||
object = Object.normalize(activity)
|
||||
|
||||
actor =
|
||||
if opts[:actor] do
|
||||
|
@ -33,7 +33,6 @@ def prepare_activity(activity, opts \\ []) do
|
|||
%{
|
||||
activity: activity,
|
||||
data: Map.get(object, :data),
|
||||
object: object,
|
||||
actor: actor
|
||||
}
|
||||
end
|
||||
|
@ -68,9 +67,7 @@ def logo(user) do
|
|||
|
||||
def last_activity(activities), do: List.last(activities)
|
||||
|
||||
def activity_object(activity), do: Object.normalize(activity)
|
||||
|
||||
def activity_title(%{data: %{"content" => content}}, opts \\ %{}) do
|
||||
def activity_title(%{"content" => content}, opts \\ %{}) do
|
||||
content
|
||||
|> Pleroma.Web.Metadata.Utils.scrub_html()
|
||||
|> Pleroma.Emoji.Formatter.demojify()
|
||||
|
@ -78,7 +75,7 @@ def activity_title(%{data: %{"content" => content}}, opts \\ %{}) do
|
|||
|> escape()
|
||||
end
|
||||
|
||||
def activity_content(%{data: %{"content" => content}}) do
|
||||
def activity_content(%{"content" => content}) do
|
||||
content
|
||||
|> String.replace(~r/[\n\r]/, "")
|
||||
|> escape()
|
||||
|
|
|
@ -17,7 +17,7 @@ defmodule Pleroma.Web.MastoFEController do
|
|||
when action == :index
|
||||
)
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index)
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action not in [:index, :manifest])
|
||||
|
||||
@doc "GET /web/*path"
|
||||
def index(%{assigns: %{user: user, token: token}} = conn, _params)
|
||||
|
|
|
@ -21,10 +21,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.ListView
|
||||
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
||||
alias Pleroma.Web.MastodonAPI.MastodonAPIController
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
|
||||
plug(:skip_plug, OAuthScopesPlug when action == :identity_proofs)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
|
||||
|
@ -101,6 +104,7 @@ def create(
|
|||
|> Map.put("fullname", params["fullname"] || nickname)
|
||||
|> Map.put("bio", params["bio"] || "")
|
||||
|> Map.put("confirm", params["password"])
|
||||
|> Map.put("trusted_app", app.trusted)
|
||||
|
||||
with :ok <- validate_email_param(params),
|
||||
{:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
|
||||
|
@ -146,9 +150,7 @@ def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
|||
end
|
||||
|
||||
@doc "PATCH /api/v1/accounts/update_credentials"
|
||||
def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
||||
user = original_user
|
||||
|
||||
def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||
user_params =
|
||||
[
|
||||
:no_rich_text,
|
||||
|
@ -184,8 +186,6 @@ def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
|||
changeset = User.update_changeset(user, user_params)
|
||||
|
||||
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)
|
||||
else
|
||||
_e -> render_error(conn, :forbidden, "Invalid request")
|
||||
|
@ -380,6 +380,8 @@ def blocks(%{assigns: %{user: user}} = conn, _) do
|
|||
end
|
||||
|
||||
@doc "GET /api/v1/endorsements"
|
||||
def endorsements(conn, params),
|
||||
do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
|
||||
def endorsements(conn, params), do: MastodonAPIController.empty_array(conn, params)
|
||||
|
||||
@doc "GET /api/v1/identity_proofs"
|
||||
def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params)
|
||||
end
|
||||
|
|
|
@ -3,21 +3,31 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
# Stubs for unimplemented mastodon api
|
||||
#
|
||||
def empty_array(conn, _) do
|
||||
Logger.debug("Unimplemented, returning an empty array")
|
||||
Logger.debug("Unimplemented, returning an empty array (list)")
|
||||
json(conn, [])
|
||||
end
|
||||
|
||||
def empty_object(conn, _) do
|
||||
Logger.debug("Unimplemented, returning an empty object")
|
||||
Logger.debug("Unimplemented, returning an empty object (map)")
|
||||
json(conn, %{})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,10 +5,13 @@
|
|||
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
|
||||
require Logger
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :index)
|
||||
|
||||
@doc "GET /api/v1/suggestions"
|
||||
def index(conn, _) do
|
||||
json(conn, [])
|
||||
end
|
||||
def index(conn, params),
|
||||
do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
|
||||
end
|
||||
|
|
|
@ -181,13 +181,11 @@ defp do_render("show.json", %{user: user} = opts) do
|
|||
bot = user.actor_type in ["Application", "Service"]
|
||||
|
||||
emojis =
|
||||
(user.source_data["tag"] || [])
|
||||
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|
||||
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
|
||||
Enum.map(user.emoji, fn {shortcode, url} ->
|
||||
%{
|
||||
"shortcode" => String.trim(name, ":"),
|
||||
"url" => MediaProxy.url(url),
|
||||
"static_url" => MediaProxy.url(url),
|
||||
"shortcode" => shortcode,
|
||||
"url" => url,
|
||||
"static_url" => url,
|
||||
"visible_in_picker" => false
|
||||
}
|
||||
end)
|
||||
|
|
|
@ -7,6 +7,21 @@ defmodule Pleroma.Web.MastodonAPI.AppView do
|
|||
|
||||
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
|
||||
%{
|
||||
id: app.id |> to_string,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.OAuth.App do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
alias Pleroma.Repo
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
@ -16,14 +17,24 @@ defmodule Pleroma.Web.OAuth.App do
|
|||
field(:website, :string)
|
||||
field(:client_id, :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()
|
||||
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
|
||||
changeset =
|
||||
struct
|
||||
|> cast(params, [:client_name, :redirect_uris, :scopes, :website])
|
||||
|> changeset(params)
|
||||
|> validate_required([:client_name, :redirect_uris, :scopes])
|
||||
|
||||
if changeset.valid? do
|
||||
|
@ -41,6 +52,21 @@ def register_changeset(struct, params \\ %{}) do
|
|||
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 """
|
||||
Gets app by attrs or create new with attrs.
|
||||
And updates the scopes if need.
|
||||
|
@ -65,4 +91,58 @@ defp update_scopes(%__MODULE__{} = app, scopes) do
|
|||
|> change(%{scopes: scopes})
|
||||
|> Repo.update()
|
||||
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
|
||||
|
|
|
@ -27,6 +27,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
plug(:fetch_flash)
|
||||
plug(RateLimiter, [name: :authentication] when action == :create_authorization)
|
||||
|
||||
plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug)
|
||||
|
||||
action_fallback(Pleroma.Web.OAuth.FallbackController)
|
||||
|
||||
@oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob"
|
||||
|
|
|
@ -13,7 +13,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
|||
alias Pleroma.Plugs.RateLimiter
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
||||
require Pleroma.Constants
|
||||
|
@ -58,38 +57,32 @@ def confirmation_resend(conn, params) do
|
|||
|
||||
@doc "PATCH /api/v1/pleroma/accounts/update_avatar"
|
||||
def update_avatar(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
|
||||
{:ok, user} =
|
||||
{:ok, _user} =
|
||||
user
|
||||
|> Changeset.change(%{avatar: nil})
|
||||
|> User.update_and_set_cache()
|
||||
|
||||
CommonAPI.update(user)
|
||||
|
||||
json(conn, %{url: nil})
|
||||
end
|
||||
|
||||
def update_avatar(%{assigns: %{user: user}} = conn, params) do
|
||||
{: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
|
||||
|
||||
CommonAPI.update(user)
|
||||
|
||||
json(conn, %{url: href})
|
||||
end
|
||||
|
||||
@doc "PATCH /api/v1/pleroma/accounts/update_banner"
|
||||
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
|
||||
with {:ok, user} <- User.update_banner(user, %{}) do
|
||||
CommonAPI.update(user)
|
||||
with {:ok, _user} <- User.update_banner(user, %{}) do
|
||||
json(conn, %{url: nil})
|
||||
end
|
||||
end
|
||||
|
||||
def update_banner(%{assigns: %{user: user}} = conn, params) do
|
||||
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
|
||||
{:ok, user} <- User.update_banner(user, object.data) do
|
||||
CommonAPI.update(user)
|
||||
{:ok, _user} <- User.update_banner(user, object.data) do
|
||||
%{"url" => [%{"href" => href} | _]} = object.data
|
||||
|
||||
json(conn, %{url: href})
|
||||
|
|
|
@ -34,7 +34,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||
|
||||
plug(
|
||||
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)
|
||||
|
|
|
@ -35,6 +35,7 @@ defmodule Pleroma.Web.Router do
|
|||
pipeline :authenticated_api do
|
||||
plug(:accepts, ["json"])
|
||||
plug(:fetch_session)
|
||||
plug(Pleroma.Plugs.AuthExpectedPlug)
|
||||
plug(Pleroma.Plugs.OAuthPlug)
|
||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
||||
plug(Pleroma.Plugs.UserFetcherPlug)
|
||||
|
@ -203,12 +204,18 @@ defmodule Pleroma.Web.Router do
|
|||
get("/config", AdminAPIController, :config_show)
|
||||
post("/config", AdminAPIController, :config_update)
|
||||
get("/config/descriptions", AdminAPIController, :config_descriptions)
|
||||
get("/need_reboot", AdminAPIController, :need_reboot)
|
||||
get("/restart", AdminAPIController, :restart)
|
||||
|
||||
get("/moderation_log", AdminAPIController, :list_log)
|
||||
|
||||
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
||||
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
|
||||
|
||||
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||
|
@ -347,7 +354,7 @@ defmodule Pleroma.Web.Router do
|
|||
get("/accounts/relationships", AccountController, :relationships)
|
||||
|
||||
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("/blocks", AccountController, :blocks)
|
||||
|
@ -680,6 +687,17 @@ defmodule Pleroma.Web.Router do
|
|||
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
|
||||
get("/user_exists", MongooseIMController, :user_exists)
|
||||
get("/check_password", MongooseIMController, :check_password)
|
||||
|
|
|
@ -18,15 +18,6 @@ defmodule Pleroma.Web.StaticFE.StaticFEView do
|
|||
|
||||
@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
|
||||
Utils.fetch_media_type(@media_types, mediaType)
|
||||
end
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
||||
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
|
||||
<id><%= @data["id"] %></id>
|
||||
<title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
||||
<content type="html"><%= activity_content(@object) %></content>
|
||||
<published><%= @data["published"] %></published>
|
||||
<updated><%= @data["published"] %></updated>
|
||||
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
||||
<content type="html"><%= activity_content(@data) %></content>
|
||||
<published><%= @activity.data["published"] %></published>
|
||||
<updated><%= @activity.data["published"] %></updated>
|
||||
<ostatus:conversation ref="<%= activity_context(@activity) %>">
|
||||
<%= activity_context(@activity) %>
|
||||
</ostatus:conversation>
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
|
||||
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
|
||||
<guid><%= @data["id"] %></guid>
|
||||
<title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
||||
<description><%= activity_content(@object) %></description>
|
||||
<pubDate><%= @data["published"] %></pubDate>
|
||||
<updated><%= @data["published"] %></updated>
|
||||
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
||||
<description><%= activity_content(@data) %></description>
|
||||
<pubDate><%= @activity.data["published"] %></pubDate>
|
||||
<updated><%= @activity.data["published"] %></updated>
|
||||
<ostatus:conversation ref="<%= activity_context(@activity) %>">
|
||||
<%= activity_context(@activity) %>
|
||||
</ostatus:conversation>
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
<%= render @view_module, "_tag_author.atom", assigns %>
|
||||
|
||||
<id><%= @data["id"] %></id>
|
||||
<title><%= activity_title(@object, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
||||
<content type="html"><%= activity_content(@object) %></content>
|
||||
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
|
||||
<content type="html"><%= activity_content(@data) %></content>
|
||||
|
||||
<%= if @activity.local do %>
|
||||
<link type="application/atom+xml" href='<%= @data["id"] %>' rel="self"/>
|
||||
|
@ -15,8 +15,8 @@
|
|||
<link type="text/html" href='<%= @data["external_url"] %>' rel="alternate"/>
|
||||
<% end %>
|
||||
|
||||
<published><%= @data["published"] %></published>
|
||||
<updated><%= @data["published"] %></updated>
|
||||
<published><%= @activity.data["published"] %></published>
|
||||
<updated><%= @activity.data["published"] %></updated>
|
||||
|
||||
<ostatus:conversation ref="<%= activity_context(@activity) %>">
|
||||
<%= activity_context(@activity) %>
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
<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>
|
||||
<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 %>
|
||||
<enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>"/>
|
||||
<% end %>
|
||||
|
||||
</item>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<img src="<%= User.avatar_url(@user) |> MediaProxy.url %>" width="48" height="48" alt="">
|
||||
</div>
|
||||
<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>
|
||||
</a>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<input type="hidden" name="profile" value="">
|
||||
<button type="submit" class="collapse">Remote follow</button>
|
||||
</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) %>
|
||||
</h3>
|
||||
<p><%= raw @user.bio %></p>
|
||||
|
|
|
@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
|
||||
def register_user(params, opts \\ []) do
|
||||
token = params["token"]
|
||||
trusted_app? = params["trusted_app"]
|
||||
|
||||
params = %{
|
||||
nickname: params["nickname"],
|
||||
|
@ -29,7 +30,7 @@ def register_user(params, opts \\ []) do
|
|||
captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled])
|
||||
# true if captcha is disabled or enabled and valid, false otherwise
|
||||
captcha_ok =
|
||||
if not captcha_enabled do
|
||||
if trusted_app? || not captcha_enabled do
|
||||
:ok
|
||||
else
|
||||
Pleroma.Captcha.validate(
|
||||
|
|
|
@ -15,6 +15,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
|
||||
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read)
|
||||
|
||||
plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token])
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
action_fallback(:errors)
|
||||
|
|
|
@ -29,11 +29,40 @@ def controller do
|
|||
import Pleroma.Web.Router.Helpers
|
||||
import Pleroma.Web.TranslationHelpers
|
||||
|
||||
alias Pleroma.Plugs.PlugHelper
|
||||
|
||||
plug(:set_put_layout)
|
||||
|
||||
defp set_put_layout(conn, _) do
|
||||
put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
|
||||
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
|
||||
|
||||
|
@ -96,6 +125,26 @@ def channel do
|
|||
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 """
|
||||
When used, dispatch to the appropriate controller/view/etc.
|
||||
"""
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 data -> data["type"] == "Emoji" and data["icon"] 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
|
|
@ -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
|
|
@ -16,6 +16,7 @@ test "transfer config values from db to env" do
|
|||
refute Application.get_env(:pleroma, :test_key)
|
||||
refute Application.get_env(:idna, :test_key)
|
||||
refute Application.get_env(:quack, :test_key)
|
||||
initial = Application.get_env(:logger, :level)
|
||||
|
||||
ConfigDB.create(%{
|
||||
group: ":pleroma",
|
||||
|
@ -35,16 +36,20 @@ test "transfer config values from db to env" do
|
|||
value: [:test_value1, :test_value2]
|
||||
})
|
||||
|
||||
ConfigDB.create(%{group: ":logger", key: ":level", value: :debug})
|
||||
|
||||
TransferTask.start_link([])
|
||||
|
||||
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(:quack, :test_key) == [:test_value1, :test_value2]
|
||||
assert Application.get_env(:logger, :level) == :debug
|
||||
|
||||
on_exit(fn ->
|
||||
Application.delete_env(:pleroma, :test_key)
|
||||
Application.delete_env(:idna, :test_key)
|
||||
Application.delete_env(:quack, :test_key)
|
||||
Application.put_env(:logger, :level, initial)
|
||||
end)
|
||||
end
|
||||
|
||||
|
@ -78,8 +83,8 @@ test "transfer config values for 1 group and some keys" do
|
|||
end
|
||||
|
||||
test "transfer config values with full subkey update" do
|
||||
emoji = Application.get_env(:pleroma, :emoji)
|
||||
assets = Application.get_env(:pleroma, :assets)
|
||||
clear_config(:emoji)
|
||||
clear_config(:assets)
|
||||
|
||||
ConfigDB.create(%{
|
||||
group: ":pleroma",
|
||||
|
@ -99,11 +104,6 @@ test "transfer config values with full subkey update" do
|
|||
assert emoji_env[:groups] == [a: 1, b: 2]
|
||||
assets_env = Application.get_env(:pleroma, :assets)
|
||||
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
|
||||
|
||||
describe "pleroma restart" do
|
||||
|
@ -112,8 +112,7 @@ test "transfer config values with full subkey update" do
|
|||
end
|
||||
|
||||
test "don't restart if no reboot time settings were changed" do
|
||||
emoji = Application.get_env(:pleroma, :emoji)
|
||||
on_exit(fn -> Application.put_env(:pleroma, :emoji, emoji) end)
|
||||
clear_config(:emoji)
|
||||
|
||||
ConfigDB.create(%{
|
||||
group: ":pleroma",
|
||||
|
@ -128,8 +127,7 @@ test "don't restart if no reboot time settings were changed" do
|
|||
end
|
||||
|
||||
test "on reboot time key" do
|
||||
chat = Application.get_env(:pleroma, :chat)
|
||||
on_exit(fn -> Application.put_env(:pleroma, :chat, chat) end)
|
||||
clear_config(:chat)
|
||||
|
||||
ConfigDB.create(%{
|
||||
group: ":pleroma",
|
||||
|
@ -141,8 +139,7 @@ test "on reboot time key" do
|
|||
end
|
||||
|
||||
test "on reboot time subkey" do
|
||||
captcha = Application.get_env(:pleroma, Pleroma.Captcha)
|
||||
on_exit(fn -> Application.put_env(:pleroma, Pleroma.Captcha, captcha) end)
|
||||
clear_config(Pleroma.Captcha)
|
||||
|
||||
ConfigDB.create(%{
|
||||
group: ":pleroma",
|
||||
|
@ -154,13 +151,8 @@ test "on reboot time subkey" do
|
|||
end
|
||||
|
||||
test "don't restart pleroma on reboot time key and subkey if there is false flag" do
|
||||
chat = Application.get_env(:pleroma, :chat)
|
||||
captcha = Application.get_env(:pleroma, Pleroma.Captcha)
|
||||
|
||||
on_exit(fn ->
|
||||
Application.put_env(:pleroma, :chat, chat)
|
||||
Application.put_env(:pleroma, Pleroma.Captcha, captcha)
|
||||
end)
|
||||
clear_config(:chat)
|
||||
clear_config(Pleroma.Captcha)
|
||||
|
||||
ConfigDB.create(%{
|
||||
group: ":pleroma",
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Emoji.FormatterTest do
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.Emoji.Formatter
|
||||
use Pleroma.DataCase
|
||||
|
||||
|
@ -32,30 +31,19 @@ test "it does not add XSS emoji" do
|
|||
end
|
||||
end
|
||||
|
||||
describe "get_emoji" do
|
||||
describe "get_emoji_map" do
|
||||
test "it returns the emoji used in the text" do
|
||||
text = "I love :firefox:"
|
||||
|
||||
assert Formatter.get_emoji(text) == [
|
||||
{"firefox",
|
||||
%Emoji{
|
||||
code: "firefox",
|
||||
file: "/emoji/Firefox.gif",
|
||||
tags: ["Gif", "Fun"],
|
||||
safe_code: "firefox",
|
||||
safe_file: "/emoji/Firefox.gif"
|
||||
}}
|
||||
]
|
||||
assert Formatter.get_emoji_map("I love :firefox:") == %{
|
||||
"firefox" => "http://localhost:4001/emoji/Firefox.gif"
|
||||
}
|
||||
end
|
||||
|
||||
test "it returns a nice empty result when no emojis are present" do
|
||||
text = "I love moominamma"
|
||||
assert Formatter.get_emoji(text) == []
|
||||
assert Formatter.get_emoji_map("I love moominamma") == %{}
|
||||
end
|
||||
|
||||
test "it doesn't die when text is absent" do
|
||||
text = nil
|
||||
assert Formatter.get_emoji(text) == []
|
||||
assert Formatter.get_emoji_map(nil) == %{}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -140,7 +140,7 @@ test "gives a replacement for user links, using local nicknames in user links te
|
|||
archaeme =
|
||||
insert(:user,
|
||||
nickname: "archa_eme_",
|
||||
source_data: %{"url" => "https://archeme/@archa_eme_"}
|
||||
uri: "https://archeme/@archa_eme_"
|
||||
)
|
||||
|
||||
archaeme_remote = insert(:user, %{nickname: "archaeme@archae.me"})
|
||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do
|
|||
|
||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Plugs.PlugHelper
|
||||
alias Pleroma.Repo
|
||||
|
||||
import Mock
|
||||
|
@ -16,6 +17,18 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do
|
|||
:ok
|
||||
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, " <>
|
||||
"proceeds with no op",
|
||||
%{conn: conn} do
|
||||
|
|
|
@ -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-----"
|
||||
|
||||
@public_key %{
|
||||
"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"
|
||||
}
|
||||
@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"
|
||||
|
||||
@rsa_public_key {
|
||||
:RSAPublicKey,
|
||||
|
@ -42,7 +37,7 @@ defp make_fake_conn(key_id),
|
|||
test "it returns key" do
|
||||
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
|
||||
end
|
||||
|
@ -53,8 +48,8 @@ test "it returns error when not found user" do
|
|||
end) =~ "[error] Could not decode user"
|
||||
end
|
||||
|
||||
test "it returns error if public key is empty" do
|
||||
user = insert(:user, source_data: %{"publicKey" => %{}})
|
||||
test "it returns error if public key is nil" do
|
||||
user = insert(:user, public_key: nil)
|
||||
|
||||
assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == {:error, :error}
|
||||
end
|
||||
|
|
|
@ -294,7 +294,7 @@ def follow_activity_factory do
|
|||
|
||||
def oauth_app_factory do
|
||||
%Pleroma.Web.OAuth.App{
|
||||
client_name: "Some client",
|
||||
client_name: sequence(:client_name, &"Some client #{&1}"),
|
||||
redirect_uris: "https://example.com/callback",
|
||||
scopes: ["read", "write", "follow", "push", "admin"],
|
||||
website: "https://example.com",
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
# 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.AppTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
setup_all do
|
||||
Mix.shell(Mix.Shell.Process)
|
||||
|
||||
on_exit(fn ->
|
||||
Mix.shell(Mix.Shell.IO)
|
||||
end)
|
||||
end
|
||||
|
||||
describe "creates new app" do
|
||||
test "with default scopes" do
|
||||
name = "Some name"
|
||||
redirect = "https://example.com"
|
||||
Mix.Tasks.Pleroma.App.run(["create", "-n", name, "-r", redirect])
|
||||
|
||||
assert_app(name, redirect, ["read", "write", "follow", "push"])
|
||||
end
|
||||
|
||||
test "with custom scopes" do
|
||||
name = "Another name"
|
||||
redirect = "https://example.com"
|
||||
|
||||
Mix.Tasks.Pleroma.App.run([
|
||||
"create",
|
||||
"-n",
|
||||
name,
|
||||
"-r",
|
||||
redirect,
|
||||
"-s",
|
||||
"read,write,follow,push,admin"
|
||||
])
|
||||
|
||||
assert_app(name, redirect, ["read", "write", "follow", "push", "admin"])
|
||||
end
|
||||
end
|
||||
|
||||
test "with errors" do
|
||||
Mix.Tasks.Pleroma.App.run(["create"])
|
||||
{:mix_shell, :error, ["Creating failed:"]}
|
||||
{:mix_shell, :error, ["name: can't be blank"]}
|
||||
{:mix_shell, :error, ["redirect_uris: can't be blank"]}
|
||||
end
|
||||
|
||||
defp assert_app(name, redirect, scopes) do
|
||||
app = Repo.get_by(Pleroma.Web.OAuth.App, client_name: name)
|
||||
|
||||
assert_received {:mix_shell, :info, [message]}
|
||||
assert message == "#{name} successfully created:"
|
||||
|
||||
assert_received {:mix_shell, :info, [message]}
|
||||
assert message == "App client_id: #{app.client_id}"
|
||||
|
||||
assert_received {:mix_shell, :info, [message]}
|
||||
assert message == "App client_secret: #{app.client_secret}"
|
||||
|
||||
assert app.scopes == scopes
|
||||
assert app.redirect_uris == redirect
|
||||
end
|
||||
end
|
|
@ -582,7 +582,7 @@ test "updates an existing user, if stale" do
|
|||
|
||||
{:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
|
||||
|
||||
assert user.source_data["endpoints"]
|
||||
assert user.inbox
|
||||
|
||||
refute user.last_refreshed_at == orig_user.last_refreshed_at
|
||||
end
|
||||
|
@ -610,7 +610,7 @@ test "returns an ap_followers link for a user" do
|
|||
) <> "/followers"
|
||||
end
|
||||
|
||||
describe "remote user creation changeset" do
|
||||
describe "remote user changeset" do
|
||||
@valid_remote %{
|
||||
bio: "hello",
|
||||
name: "Someone",
|
||||
|
@ -622,28 +622,28 @@ test "returns an ap_followers link for a user" do
|
|||
setup do: clear_config([:instance, :user_name_length])
|
||||
|
||||
test "it confirms validity" do
|
||||
cs = User.remote_user_creation(@valid_remote)
|
||||
cs = User.remote_user_changeset(@valid_remote)
|
||||
assert cs.valid?
|
||||
end
|
||||
|
||||
test "it sets the follower_adress" do
|
||||
cs = User.remote_user_creation(@valid_remote)
|
||||
cs = User.remote_user_changeset(@valid_remote)
|
||||
# remote users get a fake local follower address
|
||||
assert cs.changes.follower_address ==
|
||||
User.ap_followers(%User{nickname: @valid_remote[:nickname]})
|
||||
end
|
||||
|
||||
test "it enforces the fqn format for nicknames" do
|
||||
cs = User.remote_user_creation(%{@valid_remote | nickname: "bla"})
|
||||
cs = User.remote_user_changeset(%{@valid_remote | nickname: "bla"})
|
||||
assert Ecto.Changeset.get_field(cs, :local) == false
|
||||
assert cs.changes.avatar
|
||||
refute cs.valid?
|
||||
end
|
||||
|
||||
test "it has required fields" do
|
||||
[:name, :ap_id]
|
||||
[:ap_id]
|
||||
|> Enum.each(fn field ->
|
||||
cs = User.remote_user_creation(Map.delete(@valid_remote, field))
|
||||
cs = User.remote_user_changeset(Map.delete(@valid_remote, field))
|
||||
refute cs.valid?
|
||||
end)
|
||||
end
|
||||
|
@ -1199,58 +1199,6 @@ test "get_public_key_for_ap_id fetches a user that's not in the db" do
|
|||
assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
|
||||
end
|
||||
|
||||
describe "insert or update a user from given data" do
|
||||
test "with normal data" do
|
||||
user = insert(:user, %{nickname: "nick@name.de"})
|
||||
data = %{ap_id: user.ap_id <> "xxx", name: user.name, nickname: user.nickname}
|
||||
|
||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||
end
|
||||
|
||||
test "with overly long fields" do
|
||||
current_max_length = Pleroma.Config.get([:instance, :account_field_value_length], 255)
|
||||
user = insert(:user, nickname: "nickname@supergood.domain")
|
||||
|
||||
data = %{
|
||||
ap_id: user.ap_id,
|
||||
name: user.name,
|
||||
nickname: user.nickname,
|
||||
fields: [
|
||||
%{"name" => "myfield", "value" => String.duplicate("h", current_max_length + 1)}
|
||||
]
|
||||
}
|
||||
|
||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||
end
|
||||
|
||||
test "with an overly long bio" do
|
||||
current_max_length = Pleroma.Config.get([:instance, :user_bio_length], 5000)
|
||||
user = insert(:user, nickname: "nickname@supergood.domain")
|
||||
|
||||
data = %{
|
||||
ap_id: user.ap_id,
|
||||
name: user.name,
|
||||
nickname: user.nickname,
|
||||
bio: String.duplicate("h", current_max_length + 1)
|
||||
}
|
||||
|
||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||
end
|
||||
|
||||
test "with an overly long display name" do
|
||||
current_max_length = Pleroma.Config.get([:instance, :user_name_length], 100)
|
||||
user = insert(:user, nickname: "nickname@supergood.domain")
|
||||
|
||||
data = %{
|
||||
ap_id: user.ap_id,
|
||||
name: String.duplicate("h", current_max_length + 1),
|
||||
nickname: user.nickname
|
||||
}
|
||||
|
||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||
end
|
||||
end
|
||||
|
||||
describe "per-user rich-text filtering" do
|
||||
test "html_filter_policy returns default policies, when rich-text is enabled" do
|
||||
user = insert(:user)
|
||||
|
|
|
@ -180,7 +180,6 @@ test "it returns a user" do
|
|||
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
|
||||
assert user.ap_id == user_id
|
||||
assert user.nickname == "admin@mastodon.example.org"
|
||||
assert user.source_data
|
||||
assert user.ap_enabled
|
||||
assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
|
||||
end
|
||||
|
|
|
@ -48,10 +48,7 @@ test "it returns links" do
|
|||
|
||||
describe "determine_inbox/2" do
|
||||
test "it returns sharedInbox for messages involving as:Public in to" do
|
||||
user =
|
||||
insert(:user, %{
|
||||
source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}
|
||||
})
|
||||
user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
|
||||
|
||||
activity = %Activity{
|
||||
data: %{"to" => [@as_public], "cc" => [user.follower_address]}
|
||||
|
@ -61,10 +58,7 @@ test "it returns sharedInbox for messages involving as:Public in to" do
|
|||
end
|
||||
|
||||
test "it returns sharedInbox for messages involving as:Public in cc" do
|
||||
user =
|
||||
insert(:user, %{
|
||||
source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}
|
||||
})
|
||||
user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
|
||||
|
||||
activity = %Activity{
|
||||
data: %{"cc" => [@as_public], "to" => [user.follower_address]}
|
||||
|
@ -74,11 +68,7 @@ test "it returns sharedInbox for messages involving as:Public in cc" do
|
|||
end
|
||||
|
||||
test "it returns sharedInbox for messages involving multiple recipients in to" do
|
||||
user =
|
||||
insert(:user, %{
|
||||
source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}
|
||||
})
|
||||
|
||||
user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
|
||||
user_two = insert(:user)
|
||||
user_three = insert(:user)
|
||||
|
||||
|
@ -90,11 +80,7 @@ test "it returns sharedInbox for messages involving multiple recipients in to" d
|
|||
end
|
||||
|
||||
test "it returns sharedInbox for messages involving multiple recipients in cc" do
|
||||
user =
|
||||
insert(:user, %{
|
||||
source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}
|
||||
})
|
||||
|
||||
user = insert(:user, %{shared_inbox: "http://example.com/inbox"})
|
||||
user_two = insert(:user)
|
||||
user_three = insert(:user)
|
||||
|
||||
|
@ -107,12 +93,10 @@ test "it returns sharedInbox for messages involving multiple recipients in cc" d
|
|||
|
||||
test "it returns sharedInbox for messages involving multiple recipients in total" do
|
||||
user =
|
||||
insert(:user,
|
||||
source_data: %{
|
||||
"inbox" => "http://example.com/personal-inbox",
|
||||
"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
|
||||
}
|
||||
)
|
||||
insert(:user, %{
|
||||
shared_inbox: "http://example.com/inbox",
|
||||
inbox: "http://example.com/personal-inbox"
|
||||
})
|
||||
|
||||
user_two = insert(:user)
|
||||
|
||||
|
@ -125,12 +109,10 @@ test "it returns sharedInbox for messages involving multiple recipients in total
|
|||
|
||||
test "it returns inbox for messages involving single recipients in total" do
|
||||
user =
|
||||
insert(:user,
|
||||
source_data: %{
|
||||
"inbox" => "http://example.com/personal-inbox",
|
||||
"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
|
||||
}
|
||||
)
|
||||
insert(:user, %{
|
||||
shared_inbox: "http://example.com/inbox",
|
||||
inbox: "http://example.com/personal-inbox"
|
||||
})
|
||||
|
||||
activity = %Activity{
|
||||
data: %{"to" => [user.ap_id], "cc" => []}
|
||||
|
@ -258,11 +240,11 @@ test "it returns inbox for messages involving single recipients in total" do
|
|||
[:passthrough],
|
||||
[] do
|
||||
follower =
|
||||
insert(:user,
|
||||
insert(:user, %{
|
||||
local: false,
|
||||
source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"},
|
||||
inbox: "https://domain.com/users/nick1/inbox",
|
||||
ap_enabled: true
|
||||
)
|
||||
})
|
||||
|
||||
actor = insert(:user, follower_address: follower.ap_id)
|
||||
user = insert(:user)
|
||||
|
@ -295,14 +277,14 @@ test "it returns inbox for messages involving single recipients in total" do
|
|||
fetcher =
|
||||
insert(:user,
|
||||
local: false,
|
||||
source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"},
|
||||
inbox: "https://domain.com/users/nick1/inbox",
|
||||
ap_enabled: true
|
||||
)
|
||||
|
||||
another_fetcher =
|
||||
insert(:user,
|
||||
local: false,
|
||||
source_data: %{"inbox" => "https://domain2.com/users/nick1/inbox"},
|
||||
inbox: "https://domain2.com/users/nick1/inbox",
|
||||
ap_enabled: true
|
||||
)
|
||||
|
||||
|
|
|
@ -746,7 +746,7 @@ test "it works with custom profile fields" do
|
|||
|
||||
user = User.get_cached_by_ap_id(activity.actor)
|
||||
|
||||
assert User.fields(user) == [
|
||||
assert user.fields == [
|
||||
%{"name" => "foo", "value" => "bar"},
|
||||
%{"name" => "foo1", "value" => "bar1"}
|
||||
]
|
||||
|
@ -767,7 +767,7 @@ test "it works with custom profile fields" do
|
|||
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
|
||||
assert User.fields(user) == [
|
||||
assert user.fields == [
|
||||
%{"name" => "foo", "value" => "updated"},
|
||||
%{"name" => "foo1", "value" => "updated"}
|
||||
]
|
||||
|
@ -785,7 +785,7 @@ test "it works with custom profile fields" do
|
|||
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
|
||||
assert User.fields(user) == [
|
||||
assert user.fields == [
|
||||
%{"name" => "foo", "value" => "updated"},
|
||||
%{"name" => "foo1", "value" => "updated"}
|
||||
]
|
||||
|
@ -796,7 +796,7 @@ test "it works with custom profile fields" do
|
|||
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
|
||||
assert User.fields(user) == []
|
||||
assert user.fields == []
|
||||
end
|
||||
|
||||
test "it works for incoming update activities which lock the account" do
|
||||
|
@ -2162,4 +2162,18 @@ test "sets `replies` collection with a limited number of self-replies" do
|
|||
Transmogrifier.set_replies(object.data)["replies"]
|
||||
end
|
||||
end
|
||||
|
||||
test "take_emoji_tags/1" do
|
||||
user = insert(:user, %{emoji: %{"firefox" => "https://example.org/firefox.png"}})
|
||||
|
||||
assert Transmogrifier.take_emoji_tags(user) == [
|
||||
%{
|
||||
"icon" => %{"type" => "Image", "url" => "https://example.org/firefox.png"},
|
||||
"id" => "https://example.org/firefox.png",
|
||||
"name" => ":firefox:",
|
||||
"type" => "Emoji",
|
||||
"updated" => "1970-01-01T00:00:00Z"
|
||||
}
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,7 +29,7 @@ test "Renders profile fields" do
|
|||
|
||||
{:ok, user} =
|
||||
insert(:user)
|
||||
|> User.upgrade_changeset(%{fields: fields})
|
||||
|> User.update_changeset(%{fields: fields})
|
||||
|> User.update_and_set_cache()
|
||||
|
||||
assert %{
|
||||
|
@ -38,7 +38,7 @@ test "Renders profile fields" do
|
|||
end
|
||||
|
||||
test "Renders with emoji tags" do
|
||||
user = insert(:user, emoji: [%{"bib" => "/test"}])
|
||||
user = insert(:user, emoji: %{"bib" => "/test"})
|
||||
|
||||
assert %{
|
||||
"tag" => [
|
||||
|
|
|
@ -2110,7 +2110,7 @@ test "saving config which need pleroma reboot", %{conn: conn} do
|
|||
|> get("/api/pleroma/admin/config")
|
||||
|> json_response(200)
|
||||
|
||||
refute Map.has_key?(configs, "need_reboot")
|
||||
assert configs["need_reboot"] == false
|
||||
end
|
||||
|
||||
test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
|
||||
|
@ -2166,7 +2166,7 @@ test "update setting which need reboot, don't change reboot flag until reboot",
|
|||
|> get("/api/pleroma/admin/config")
|
||||
|> json_response(200)
|
||||
|
||||
refute Map.has_key?(configs, "need_reboot")
|
||||
assert configs["need_reboot"] == false
|
||||
end
|
||||
|
||||
test "saving config with nested merge", %{conn: conn} do
|
||||
|
@ -2861,6 +2861,20 @@ test "pleroma restarts", %{conn: conn} do
|
|||
end
|
||||
end
|
||||
|
||||
test "need_reboot flag", %{conn: conn} do
|
||||
assert conn
|
||||
|> get("/api/pleroma/admin/need_reboot")
|
||||
|> json_response(200) == %{"need_reboot" => false}
|
||||
|
||||
Restarter.Pleroma.need_reboot()
|
||||
|
||||
assert conn
|
||||
|> get("/api/pleroma/admin/need_reboot")
|
||||
|> json_response(200) == %{"need_reboot" => true}
|
||||
|
||||
on_exit(fn -> Restarter.Pleroma.refresh() end)
|
||||
end
|
||||
|
||||
describe "GET /api/pleroma/admin/statuses" do
|
||||
test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
|
||||
blocked = insert(:user)
|
||||
|
@ -3503,6 +3517,191 @@ test "status visibility count", %{conn: conn} do
|
|||
response["status_visibility"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /api/pleroma/admin/oauth_app" do
|
||||
test "errors", %{conn: conn} do
|
||||
response = conn |> post("/api/pleroma/admin/oauth_app", %{}) |> json_response(200)
|
||||
|
||||
assert response == %{"name" => "can't be blank", "redirect_uris" => "can't be blank"}
|
||||
end
|
||||
|
||||
test "success", %{conn: conn} do
|
||||
base_url = Pleroma.Web.base_url()
|
||||
app_name = "Trusted app"
|
||||
|
||||
response =
|
||||
conn
|
||||
|> post("/api/pleroma/admin/oauth_app", %{
|
||||
name: app_name,
|
||||
redirect_uris: base_url
|
||||
})
|
||||
|> json_response(200)
|
||||
|
||||
assert %{
|
||||
"client_id" => _,
|
||||
"client_secret" => _,
|
||||
"name" => ^app_name,
|
||||
"redirect_uri" => ^base_url,
|
||||
"trusted" => false
|
||||
} = response
|
||||
end
|
||||
|
||||
test "with trusted", %{conn: conn} do
|
||||
base_url = Pleroma.Web.base_url()
|
||||
app_name = "Trusted app"
|
||||
|
||||
response =
|
||||
conn
|
||||
|> post("/api/pleroma/admin/oauth_app", %{
|
||||
name: app_name,
|
||||
redirect_uris: base_url,
|
||||
trusted: true
|
||||
})
|
||||
|> json_response(200)
|
||||
|
||||
assert %{
|
||||
"client_id" => _,
|
||||
"client_secret" => _,
|
||||
"name" => ^app_name,
|
||||
"redirect_uri" => ^base_url,
|
||||
"trusted" => true
|
||||
} = response
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /api/pleroma/admin/oauth_app" do
|
||||
setup do
|
||||
app = insert(:oauth_app)
|
||||
{:ok, app: app}
|
||||
end
|
||||
|
||||
test "list", %{conn: conn} do
|
||||
response =
|
||||
conn
|
||||
|> get("/api/pleroma/admin/oauth_app")
|
||||
|> json_response(200)
|
||||
|
||||
assert %{"apps" => apps, "count" => count, "page_size" => _} = response
|
||||
|
||||
assert length(apps) == count
|
||||
end
|
||||
|
||||
test "with page size", %{conn: conn} do
|
||||
insert(:oauth_app)
|
||||
page_size = 1
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/pleroma/admin/oauth_app", %{page_size: to_string(page_size)})
|
||||
|> json_response(200)
|
||||
|
||||
assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response
|
||||
|
||||
assert length(apps) == page_size
|
||||
end
|
||||
|
||||
test "search by client name", %{conn: conn, app: app} do
|
||||
response =
|
||||
conn
|
||||
|> get("/api/pleroma/admin/oauth_app", %{name: app.client_name})
|
||||
|> json_response(200)
|
||||
|
||||
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
||||
|
||||
assert returned["client_id"] == app.client_id
|
||||
assert returned["name"] == app.client_name
|
||||
end
|
||||
|
||||
test "search by client id", %{conn: conn, app: app} do
|
||||
response =
|
||||
conn
|
||||
|> get("/api/pleroma/admin/oauth_app", %{client_id: app.client_id})
|
||||
|> json_response(200)
|
||||
|
||||
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
||||
|
||||
assert returned["client_id"] == app.client_id
|
||||
assert returned["name"] == app.client_name
|
||||
end
|
||||
|
||||
test "only trusted", %{conn: conn} do
|
||||
app = insert(:oauth_app, trusted: true)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/pleroma/admin/oauth_app", %{trusted: true})
|
||||
|> json_response(200)
|
||||
|
||||
assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
|
||||
|
||||
assert returned["client_id"] == app.client_id
|
||||
assert returned["name"] == app.client_name
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE /api/pleroma/admin/oauth_app/:id" do
|
||||
test "with id", %{conn: conn} do
|
||||
app = insert(:oauth_app)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id))
|
||||
|> json_response(:no_content)
|
||||
|
||||
assert response == ""
|
||||
end
|
||||
|
||||
test "with non existance id", %{conn: conn} do
|
||||
response =
|
||||
conn
|
||||
|> delete("/api/pleroma/admin/oauth_app/0")
|
||||
|> json_response(:bad_request)
|
||||
|
||||
assert response == ""
|
||||
end
|
||||
end
|
||||
|
||||
describe "PATCH /api/pleroma/admin/oauth_app/:id" do
|
||||
test "with id", %{conn: conn} do
|
||||
app = insert(:oauth_app)
|
||||
|
||||
name = "another name"
|
||||
url = "https://example.com"
|
||||
scopes = ["admin"]
|
||||
id = app.id
|
||||
website = "http://website.com"
|
||||
|
||||
response =
|
||||
conn
|
||||
|> patch("/api/pleroma/admin/oauth_app/" <> to_string(app.id), %{
|
||||
name: name,
|
||||
trusted: true,
|
||||
redirect_uris: url,
|
||||
scopes: scopes,
|
||||
website: website
|
||||
})
|
||||
|> json_response(200)
|
||||
|
||||
assert %{
|
||||
"client_id" => _,
|
||||
"client_secret" => _,
|
||||
"id" => ^id,
|
||||
"name" => ^name,
|
||||
"redirect_uri" => ^url,
|
||||
"trusted" => true,
|
||||
"website" => ^website
|
||||
} = response
|
||||
end
|
||||
|
||||
test "without id", %{conn: conn} do
|
||||
response =
|
||||
conn
|
||||
|> patch("/api/pleroma/admin/oauth_app/0")
|
||||
|> json_response(:bad_request)
|
||||
|
||||
assert response == ""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Needed for testing
|
||||
|
|
|
@ -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 Pleroma.Tests.OAuthTestControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
setup %{conn: conn} do
|
||||
user = insert(:user)
|
||||
conn = assign(conn, :user, user)
|
||||
%{conn: conn, user: user}
|
||||
end
|
||||
|
||||
test "missed_oauth", %{conn: conn} do
|
||||
res =
|
||||
conn
|
||||
|> get("/test/authenticated_api/missed_oauth")
|
||||
|> json_response(403)
|
||||
|
||||
assert res ==
|
||||
%{
|
||||
"error" =>
|
||||
"Security violation: OAuth scopes check was neither handled nor explicitly skipped."
|
||||
}
|
||||
end
|
||||
|
||||
test "skipped_oauth", %{conn: conn} do
|
||||
conn
|
||||
|> assign(:token, nil)
|
||||
|> get("/test/authenticated_api/skipped_oauth")
|
||||
|> json_response(200)
|
||||
end
|
||||
|
||||
test "performed_oauth", %{user: user} do
|
||||
%{conn: good_token_conn} = oauth_access(["read"], user: user)
|
||||
|
||||
good_token_conn
|
||||
|> get("/test/authenticated_api/performed_oauth")
|
||||
|> json_response(200)
|
||||
|
||||
%{conn: bad_token_conn} = oauth_access(["follow"], user: user)
|
||||
|
||||
bad_token_conn
|
||||
|> get("/test/authenticated_api/performed_oauth")
|
||||
|> json_response(403)
|
||||
end
|
||||
end
|
|
@ -125,18 +125,6 @@ test "it adds emoji in the object" do
|
|||
assert Object.normalize(activity).data["emoji"]["firefox"]
|
||||
end
|
||||
|
||||
test "it adds emoji when updating profiles" do
|
||||
user = insert(:user, %{name: ":firefox:"})
|
||||
|
||||
{:ok, activity} = CommonAPI.update(user)
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
[firefox] = user.source_data["tag"]
|
||||
|
||||
assert firefox["name"] == ":firefox:"
|
||||
|
||||
assert Pleroma.Constants.as_public() in activity.recipients
|
||||
end
|
||||
|
||||
describe "posting" do
|
||||
test "it supports explicit addressing" do
|
||||
user = insert(:user)
|
||||
|
|
|
@ -7,7 +7,6 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
|||
alias Pleroma.Object
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
alias Pleroma.Web.Endpoint
|
||||
use Pleroma.DataCase
|
||||
|
||||
import ExUnit.CaptureLog
|
||||
|
@ -42,28 +41,6 @@ test "correct password given" do
|
|||
end
|
||||
end
|
||||
|
||||
test "parses emoji from name and bio" do
|
||||
{:ok, user} = UserBuilder.insert(%{name: ":blank:", bio: ":firefox:"})
|
||||
|
||||
expected = [
|
||||
%{
|
||||
"type" => "Emoji",
|
||||
"icon" => %{"type" => "Image", "url" => "#{Endpoint.url()}/emoji/Firefox.gif"},
|
||||
"name" => ":firefox:"
|
||||
},
|
||||
%{
|
||||
"type" => "Emoji",
|
||||
"icon" => %{
|
||||
"type" => "Image",
|
||||
"url" => "#{Endpoint.url()}/emoji/blank.png"
|
||||
},
|
||||
"name" => ":blank:"
|
||||
}
|
||||
]
|
||||
|
||||
assert expected == Utils.emoji_from_profile(user)
|
||||
end
|
||||
|
||||
describe "format_input/3" do
|
||||
test "works for bare text/plain" do
|
||||
text = "hello world!"
|
||||
|
|
|
@ -78,7 +78,7 @@ test "it federates only to reachable instances via AP" do
|
|||
local: false,
|
||||
nickname: "nick1@domain.com",
|
||||
ap_id: "https://domain.com/users/nick1",
|
||||
source_data: %{"inbox" => inbox1},
|
||||
inbox: inbox1,
|
||||
ap_enabled: true
|
||||
})
|
||||
|
||||
|
@ -86,7 +86,7 @@ test "it federates only to reachable instances via AP" do
|
|||
local: false,
|
||||
nickname: "nick2@domain2.com",
|
||||
ap_id: "https://domain2.com/users/nick2",
|
||||
source_data: %{"inbox" => inbox2},
|
||||
inbox: inbox2,
|
||||
ap_enabled: true
|
||||
})
|
||||
|
||||
|
|
|
@ -150,8 +150,8 @@ test "gets a feed (RSS)", %{conn: conn} do
|
|||
obj2 = Object.normalize(activity2)
|
||||
|
||||
assert xpath(xml, ~x"//channel/item/description/text()"sl) == [
|
||||
HtmlEntities.decode(FeedView.activity_content(obj2)),
|
||||
HtmlEntities.decode(FeedView.activity_content(obj1))
|
||||
HtmlEntities.decode(FeedView.activity_content(obj2.data)),
|
||||
HtmlEntities.decode(FeedView.activity_content(obj1.data))
|
||||
]
|
||||
|
||||
response =
|
||||
|
|
|
@ -944,6 +944,73 @@ test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_
|
|||
res = post(conn, "/api/v1/accounts", valid_params)
|
||||
assert json_response(res, 403) == %{"error" => "Invalid credentials"}
|
||||
end
|
||||
|
||||
test "registration from trusted app" do
|
||||
clear_config([Pleroma.Captcha, :enabled], true)
|
||||
app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"])
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> post("/oauth/token", %{
|
||||
"grant_type" => "client_credentials",
|
||||
"client_id" => app.client_id,
|
||||
"client_secret" => app.client_secret
|
||||
})
|
||||
|
||||
assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200)
|
||||
|
||||
response =
|
||||
build_conn()
|
||||
|> Plug.Conn.put_req_header("authorization", "Bearer " <> token)
|
||||
|> post("/api/v1/accounts", %{
|
||||
nickname: "nickanme",
|
||||
agreement: true,
|
||||
email: "email@example.com",
|
||||
fullname: "Lain",
|
||||
username: "Lain",
|
||||
password: "some_password",
|
||||
confirm: "some_password"
|
||||
})
|
||||
|> json_response(200)
|
||||
|
||||
assert %{
|
||||
"access_token" => access_token,
|
||||
"created_at" => _,
|
||||
"scope" => ["read", "write", "follow", "push"],
|
||||
"token_type" => "Bearer"
|
||||
} = response
|
||||
|
||||
response =
|
||||
build_conn()
|
||||
|> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token)
|
||||
|> get("/api/v1/accounts/verify_credentials")
|
||||
|> json_response(200)
|
||||
|
||||
assert %{
|
||||
"acct" => "Lain",
|
||||
"bot" => false,
|
||||
"display_name" => "Lain",
|
||||
"follow_requests_count" => 0,
|
||||
"followers_count" => 0,
|
||||
"following_count" => 0,
|
||||
"locked" => false,
|
||||
"note" => "",
|
||||
"source" => %{
|
||||
"fields" => [],
|
||||
"note" => "",
|
||||
"pleroma" => %{
|
||||
"actor_type" => "Person",
|
||||
"discoverable" => false,
|
||||
"no_rich_text" => false,
|
||||
"show_role" => true
|
||||
},
|
||||
"privacy" => "public",
|
||||
"sensitive" => false
|
||||
},
|
||||
"statuses_count" => 0,
|
||||
"username" => "Lain"
|
||||
} = response
|
||||
end
|
||||
end
|
||||
|
||||
describe "create account by app / rate limit" do
|
||||
|
|
|
@ -7,34 +7,8 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do
|
|||
|
||||
alias Pleroma.Config
|
||||
|
||||
import Pleroma.Factory
|
||||
import Tesla.Mock
|
||||
|
||||
setup do: oauth_access(["read"])
|
||||
|
||||
setup %{user: user} do
|
||||
other_user = insert(:user)
|
||||
host = Config.get([Pleroma.Web.Endpoint, :url, :host])
|
||||
url500 = "http://test500?#{host}&#{user.nickname}"
|
||||
url200 = "http://test200?#{host}&#{user.nickname}"
|
||||
|
||||
mock(fn
|
||||
%{method: :get, url: ^url500} ->
|
||||
%Tesla.Env{status: 500, body: "bad request"}
|
||||
|
||||
%{method: :get, url: ^url200} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body:
|
||||
~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
|
||||
other_user.ap_id
|
||||
}","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
|
||||
}
|
||||
end)
|
||||
|
||||
[other_user: other_user]
|
||||
end
|
||||
|
||||
test "returns empty result", %{conn: conn} do
|
||||
res =
|
||||
conn
|
||||
|
|
|
@ -19,16 +19,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
|
|||
end
|
||||
|
||||
test "Represent a user account" do
|
||||
source_data = %{
|
||||
"tag" => [
|
||||
%{
|
||||
"type" => "Emoji",
|
||||
"icon" => %{"url" => "/file.png"},
|
||||
"name" => ":karjalanpiirakka:"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
background_image = %{
|
||||
"url" => [%{"href" => "https://example.com/images/asuka_hospital.png"}]
|
||||
}
|
||||
|
@ -37,13 +27,13 @@ test "Represent a user account" do
|
|||
insert(:user, %{
|
||||
follower_count: 3,
|
||||
note_count: 5,
|
||||
source_data: source_data,
|
||||
background: background_image,
|
||||
nickname: "shp@shitposter.club",
|
||||
name: ":karjalanpiirakka: shp",
|
||||
bio:
|
||||
"<script src=\"invalid-html\"></script><span>valid html</span>. a<br>b<br/>c<br >d<br />f",
|
||||
inserted_at: ~N[2017-08-15 15:47:06.597036]
|
||||
inserted_at: ~N[2017-08-15 15:47:06.597036],
|
||||
emoji: %{"karjalanpiirakka" => "/file.png"}
|
||||
})
|
||||
|
||||
expected = %{
|
||||
|
@ -118,7 +108,6 @@ test "Represent a Service(bot) account" do
|
|||
insert(:user, %{
|
||||
follower_count: 3,
|
||||
note_count: 5,
|
||||
source_data: %{},
|
||||
actor_type: "Service",
|
||||
nickname: "shp@shitposter.club",
|
||||
inserted_at: ~N[2017-08-15 15:47:06.597036]
|
||||
|
@ -313,7 +302,6 @@ test "represent an embedded relationship" do
|
|||
insert(:user, %{
|
||||
follower_count: 0,
|
||||
note_count: 5,
|
||||
source_data: %{},
|
||||
actor_type: "Service",
|
||||
nickname: "shp@shitposter.club",
|
||||
inserted_at: ~N[2017-08-15 15:47:06.597036]
|
||||
|
|
|
@ -220,7 +220,7 @@ test "PATCH /api/v1/pleroma/conversations/:id" do
|
|||
|
||||
test "POST /api/v1/pleroma/conversations/read" do
|
||||
user = insert(:user)
|
||||
%{user: other_user, conn: conn} = oauth_access(["write:notifications"])
|
||||
%{user: other_user, conn: conn} = oauth_access(["write:conversations"])
|
||||
|
||||
{:ok, _activity} =
|
||||
CommonAPI.post(user, %{"status" => "Hi @#{other_user.nickname}", "visibility" => "direct"})
|
||||
|
|
Loading…
Reference in New Issue