Merge remote-tracking branch 'upstream/develop' into oauth-form

This commit is contained in:
Alex Gleason 2020-08-05 15:53:37 -05:00
commit 730bc616e3
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
49 changed files with 999 additions and 192 deletions

View File

@ -49,6 +49,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added
- Configuration: Added a blacklist for email servers.
- Chats: Added `accepts_chat_messages` field to user, exposed in APIs and federation.
- Chats: Added support for federated chats. For details, see the docs.
- ActivityPub: Added support for existing AP ids for instances migrated from Mastodon.
@ -69,7 +70,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Support for viewing instances favicons next to posts and accounts
- Added Pleroma.Upload.Filter.Exiftool as an alternate EXIF stripping mechanism targeting GPS/location metadata.
- "By approval" registrations mode.
- Configuration: Added `:welcome` settings for the welcome message to newly registered users.
- Configuration: Added `:welcome` settings for the welcome message to newly registered users. You can send a welcome message as a direct message, chat or email.
- Ability to hide favourites and emoji reactions in the API with `[:instance, :show_reactions]` config.
<details>

View File

@ -172,7 +172,7 @@ config :mime, :types, %{
"application/ld+json" => ["activity+json"]
}
config :tesla, adapter: Tesla.Adapter.Gun
config :tesla, adapter: Tesla.Adapter.Hackney
# Configures http settings, upstream proxy etc.
config :pleroma, :http,
@ -261,6 +261,11 @@ config :pleroma, :welcome,
sender_nickname: nil,
message: nil
],
chat_message: [
enabled: false,
sender_nickname: nil,
message: nil
],
email: [
enabled: false,
sender: nil,
@ -374,6 +379,7 @@ config :pleroma, :mrf_simple,
federated_timeline_removal: [],
report_removal: [],
reject: [],
followers_only: [],
accept: [],
avatar_removal: [],
banner_removal: [],
@ -392,8 +398,9 @@ config :pleroma, :mrf_vocabulary,
accept: [],
reject: []
# threshold of 7 days
config :pleroma, :mrf_object_age,
threshold: 172_800,
threshold: 604_800,
actions: [:delist, :strip_followers]
config :pleroma, :rich_media,
@ -509,7 +516,8 @@ config :pleroma, Pleroma.User,
"user_exists",
"users",
"web"
]
],
email_blacklist: []
config :pleroma, Oban,
repo: Pleroma.Repo,
@ -719,7 +727,7 @@ config :pleroma, :restrict_unauthenticated,
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false
config :pleroma, :mrf,
policies: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
policies: Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy,
transparency: true,
transparency_exclusions: []

View File

@ -998,6 +998,35 @@ config :pleroma, :config_description, [
]
},
%{
group: :chat_message,
type: :group,
descpiption: "Chat message settings",
children: [
%{
key: :enabled,
type: :boolean,
description: "Enables sends chat message for new user after registration"
},
%{
key: :message,
type: :string,
description:
"A message that will be sent to a newly registered users as a chat message",
suggestions: [
"Hello, welcome on board!"
]
},
%{
key: :sender_nickname,
type: :string,
description: "The nickname of the local user that sends the welcome message",
suggestions: [
"lain"
]
}
]
},
%{
group: :email,
type: :group,
descpiption: "Email message settings",
@ -1543,6 +1572,12 @@ config :pleroma, :config_description, [
suggestions: ["example.com", "*.example.com"]
},
%{
key: :followers_only,
type: {:list, :string},
description: "Force posts from the given instances to be visible by followers only",
suggestions: ["example.com", "*.example.com"]
},
%{
key: :report_removal,
type: {:list, :string},
description: "List of instances to reject reports from",
@ -3021,6 +3056,7 @@ config :pleroma, :config_description, [
%{
key: :restricted_nicknames,
type: {:list, :string},
description: "List of nicknames users may not register with.",
suggestions: [
".well-known",
"~",
@ -3053,6 +3089,12 @@ config :pleroma, :config_description, [
"users",
"web"
]
},
%{
key: :email_blacklist,
type: {:list, :string},
description: "List of email domains users may not register with.",
suggestions: ["mailinator.com", "maildrop.cc"]
}
]
},

View File

@ -120,6 +120,8 @@ config :pleroma, Pleroma.Uploaders.S3,
config :tzdata, :autoupdate, :disabled
config :pleroma, :mrf, policies: []
if File.exists?("./config/test.secret.exs") do
import_config "test.secret.exs"
else

View File

@ -50,7 +50,7 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi
* Authentication: not required
* Params: none
* Response: Provider specific JSON, the only guaranteed parameter is `type`
* Example response: `{"type": "kocaptcha", "token": "whatever", "url": "https://captcha.kotobank.ch/endpoint", seconds_valid: 300}`
* Example response: `{"type": "kocaptcha", "token": "whatever", "url": "https://captcha.kotobank.ch/endpoint", "seconds_valid": 300}`
## `/api/pleroma/delete_account`
### Delete an account

View File

@ -75,6 +75,13 @@ Feel free to contact us to be added to this list!
- Platform: Android, iOS
- Features: No Streaming
### Indigenous
- Homepage: <https://indigenous.realize.be/>
- Source Code: <https://github.com/swentel/indigenous-android/>
- Contact: [@realize.be@realize.be](@realize.be@realize.be)
- Platforms: Android
- Features: No Streaming
## Alternative Web Interfaces
### Brutaldon
- Homepage: <https://jfm.carcosa.net/projects/software/brutaldon/>

View File

@ -69,6 +69,10 @@ To add configuration to your config file, you can copy it from the base config.
* `enabled`: Enables the send a direct message to a newly registered user. Defaults to `false`.
* `sender_nickname`: The nickname of the local user that sends the welcome message.
* `message`: A message that will be send to a newly registered users as a direct message.
* `chat_message`: - welcome message sent as a chat message.
* `enabled`: Enables the send a chat message to a newly registered user. Defaults to `false`.
* `sender_nickname`: The nickname of the local user that sends the welcome message.
* `message`: A message that will be send to a newly registered users as a chat message.
* `email`: - welcome message sent as a email.
* `enabled`: Enables the send a welcome email to a newly registered user. Defaults to `false`.
* `sender`: The email address or tuple with `{nickname, email}` that will use as sender to the welcome email.
@ -125,6 +129,7 @@ To add configuration to your config file, you can copy it from the base config.
* `federated_timeline_removal`: List of instances to remove from Federated (aka The Whole Known Network) Timeline.
* `reject`: List of instances to reject any activities from.
* `accept`: List of instances to accept any activities from.
* `followers_only`: List of instances to decrease post visibility to only the followers, including for DM mentions.
* `report_removal`: List of instances to reject reports from.
* `avatar_removal`: List of instances to strip avatars from.
* `banner_removal`: List of instances to strip banners from.
@ -202,6 +207,11 @@ config :pleroma, :mrf_user_allowlist, %{
* `sign_object_fetches`: Sign object fetches with HTTP signatures
* `authorized_fetch_mode`: Require HTTP signatures for AP fetches
## Pleroma.User
* `restricted_nicknames`: List of nicknames users may not register with.
* `email_blacklist`: List of email domains users may not register with.
## Pleroma.ScheduledActivity
* `daily_user_limit`: the number of scheduled activities a user is allowed to create in a single day (Default: `25`)

View File

@ -5,13 +5,7 @@ The configuration of Pleroma has traditionally been managed with a config file,
## Migration to database config
1. Stop your Pleroma instance and edit your Pleroma config to enable database configuration:
```
config :pleroma, configurable_from_database: true
```
2. Run the mix task to migrate to the database. You'll receive some debugging output and a few messages informing you of what happened.
1. Run the mix task to migrate to the database. You'll receive some debugging output and a few messages informing you of what happened.
**Source:**
@ -23,6 +17,8 @@ The configuration of Pleroma has traditionally been managed with a config file,
**OTP:**
*Note: OTP users need Pleroma to be running for `pleroma_ctl` commands to work*
```
$ ./bin/pleroma_ctl config migrate_to_db
```
@ -47,53 +43,56 @@ The configuration of Pleroma has traditionally been managed with a config file,
Settings for group :pleroma migrated.
```
2. It is recommended to backup your config file now.
3. It is recommended to backup your config file now.
```
cp config/dev.secret.exs config/dev.secret.exs.orig
```
4. Now you can edit your config file and strip it down to the only settings which are not possible to control in the database. e.g., the Postgres and webserver (Endpoint) settings cannot be controlled in the database because the application needs the settings to start up and access the database.
3. Edit your Pleroma config to enable database configuration:
⚠️ **THIS IS NOT REQUIRED**
```
config :pleroma, configurable_from_database: true
```
Any settings in the database will override those in the config file, but you may find it less confusing if the setting is only declared in one place.
4. ⚠️ **THIS IS NOT REQUIRED** ⚠️
A non-exhaustive list of settings that are only possible in the config file include the following:
Now you can edit your config file and strip it down to the only settings which are not possible to control in the database. e.g., the Postgres (Repo) and webserver (Endpoint) settings cannot be controlled in the database because the application needs the settings to start up and access the database.
* config :pleroma, Pleroma.Web.Endpoint
* config :pleroma, Pleroma.Repo
* config :pleroma, configurable_from_database
* config :pleroma, :database, rum_enabled
* config :pleroma, :connections_pool
Any settings in the database will override those in the config file, but you may find it less confusing if the setting is only declared in one place.
Here is an example of a server config stripped down after migration:
A non-exhaustive list of settings that are only possible in the config file include the following:
```
use Mix.Config
* config :pleroma, Pleroma.Web.Endpoint
* config :pleroma, Pleroma.Repo
* config :pleroma, configurable\_from\_database
* config :pleroma, :database, rum_enabled
* config :pleroma, :connections_pool
config :pleroma, Pleroma.Web.Endpoint,
url: [host: "cool.pleroma.site", scheme: "https", port: 443]
Here is an example of a server config stripped down after migration:
```
use Mix.Config
config :pleroma, Pleroma.Repo,
adapter: Ecto.Adapters.Postgres,
username: "pleroma",
password: "MySecretPassword",
database: "pleroma_prod",
hostname: "localhost"
config :pleroma, Pleroma.Web.Endpoint,
url: [host: "cool.pleroma.site", scheme: "https", port: 443]
config :pleroma, configurable_from_database: true
```
config :pleroma, Pleroma.Repo,
adapter: Ecto.Adapters.Postgres,
username: "pleroma",
password: "MySecretPassword",
database: "pleroma_prod",
hostname: "localhost"
5. Start your instance back up and you can now access the Settings tab in AdminFE.
config :pleroma, configurable_from_database: true
```
5. Restart your instance and you can now access the Settings tab in AdminFE.
## Reverting back from database config
1. Stop your Pleroma instance.
2. Run the mix task to migrate back from the database. You'll receive some debugging output and a few messages informing you of what happened.
1. Run the mix task to migrate back from the database. You'll receive some debugging output and a few messages informing you of what happened.
**Source:**
@ -118,7 +117,9 @@ config :pleroma, configurable_from_database: true
Database configuration settings have been saved to config/dev.exported_from_db.secret.exs
```
3. The in-database configuration still exists, but it will not be used if you remove `config :pleroma, configurable_from_database: true` from your config.
2. Remove `config :pleroma, configurable_from_database: true` from your config. The in-database configuration still exists, but it will not be used. Future migrations will erase the database config before importing your config file again.
3. Restart your instance.
## Debugging

View File

@ -11,12 +11,10 @@ defmodule Pleroma.Config do
def get([key], default), do: get(key, default)
def get([parent_key | keys], default) do
case :pleroma
|> Application.get_env(parent_key)
|> get_in(keys) do
nil -> default
any -> any
def get([_ | _] = path, default) do
case fetch(path) do
{:ok, value} -> value
:error -> default
end
end
@ -34,6 +32,24 @@ defmodule Pleroma.Config do
end
end
def fetch(key) when is_atom(key), do: fetch([key])
def fetch([root_key | keys]) do
Enum.reduce_while(keys, Application.fetch_env(:pleroma, root_key), fn
key, {:ok, config} when is_map(config) or is_list(config) ->
case Access.fetch(config, key) do
:error ->
{:halt, :error}
value ->
{:cont, value}
end
_key, _config ->
{:halt, :error}
end)
end
def put([key], value), do: put(key, value)
def put([parent_key | keys], value) do
@ -50,12 +66,15 @@ defmodule Pleroma.Config do
def delete([key]), do: delete(key)
def delete([parent_key | keys]) do
{_, parent} =
Application.get_env(:pleroma, parent_key)
|> get_and_update_in(keys, fn _ -> :pop end)
def delete([parent_key | keys] = path) do
with {:ok, _} <- fetch(path) do
{_, parent} =
parent_key
|> get()
|> get_and_update_in(keys, fn _ -> :pop end)
Application.put_env(:pleroma, parent_key, parent)
Application.put_env(:pleroma, parent_key, parent)
end
end
def delete(key) do

View File

@ -95,7 +95,11 @@ defmodule Pleroma.FollowingRelationship do
|> where([r], r.state == ^:follow_accept)
end
def followers_ap_ids(%User{} = user, from_ap_ids \\ nil) do
def followers_ap_ids(user, from_ap_ids \\ nil)
def followers_ap_ids(_, []), do: []
def followers_ap_ids(%User{} = user, from_ap_ids) do
query =
user
|> followers_query()

View File

@ -10,6 +10,7 @@ defmodule Pleroma.Gun.ConnectionPool do
]
end
@spec get_conn(URI.t(), keyword()) :: {:ok, pid()} | {:error, term()}
def get_conn(uri, opts) do
key = "#{uri.scheme}:#{uri.host}:#{uri.port}"
@ -54,12 +55,14 @@ defmodule Pleroma.Gun.ConnectionPool do
{:DOWN, ^ref, :process, ^worker_pid, reason} ->
case reason do
{:shutdown, error} -> error
{:shutdown, {:error, _} = error} -> error
{:shutdown, error} -> {:error, error}
_ -> {:error, reason}
end
end
end
@spec release_conn(pid()) :: :ok
def release_conn(conn_pid) do
# :ets.fun2ms(fn {_, {worker_pid, {gun_pid, _, _, _}}} when gun_pid == conn_pid ->
# worker_pid end)

View File

@ -34,10 +34,12 @@ defmodule Pleroma.HTTP.RequestBuilder do
@spec headers(Request.t(), Request.headers()) :: Request.t()
def headers(request, headers) do
headers_list =
if Pleroma.Config.get([:http, :send_user_agent]) do
with true <- Pleroma.Config.get([:http, :send_user_agent]),
nil <- Enum.find(headers, fn {key, _val} -> String.downcase(key) == "user-agent" end) do
[{"user-agent", Pleroma.Application.user_agent()} | headers]
else
headers
_ ->
headers
end
%{request | headers: headers_list}

View File

@ -676,10 +676,19 @@ defmodule Pleroma.User do
|> validate_required([:name, :nickname, :password, :password_confirmation])
|> validate_confirmation(:password)
|> unique_constraint(:email)
|> validate_format(:email, @email_regex)
|> validate_change(:email, fn :email, email ->
valid? =
Config.get([User, :email_blacklist])
|> Enum.all?(fn blacklisted_domain ->
!String.ends_with?(email, ["@" <> blacklisted_domain, "." <> blacklisted_domain])
end)
if valid?, do: [], else: [email: "Invalid email"]
end)
|> unique_constraint(:nickname)
|> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
|> validate_format(:nickname, local_nickname_regex())
|> validate_format(:email, @email_regex)
|> validate_length(:bio, max: bio_limit)
|> validate_length(:name, min: 1, max: name_limit)
|> validate_length(:registration_reason, max: reason_limit)
@ -734,6 +743,7 @@ defmodule Pleroma.User do
{:ok, user} <- set_cache(user),
{:ok, _} <- send_welcome_email(user),
{:ok, _} <- send_welcome_message(user),
{:ok, _} <- send_welcome_chat_message(user),
{:ok, _} <- try_send_confirmation_email(user) do
{:ok, user}
end
@ -748,6 +758,15 @@ defmodule Pleroma.User do
end
end
def send_welcome_chat_message(user) do
if User.WelcomeChatMessage.enabled?() do
User.WelcomeChatMessage.post_message(user)
{:ok, :enqueued}
else
{:ok, :noop}
end
end
def send_welcome_email(%User{email: email} = user) when is_binary(email) do
if User.WelcomeEmail.enabled?() do
User.WelcomeEmail.send_email(user)

View File

@ -0,0 +1,45 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.WelcomeChatMessage do
alias Pleroma.Config
alias Pleroma.User
alias Pleroma.Web.CommonAPI
@spec enabled?() :: boolean()
def enabled?, do: Config.get([:welcome, :chat_message, :enabled], false)
@spec post_message(User.t()) :: {:ok, Pleroma.Activity.t() | nil}
def post_message(user) do
[:welcome, :chat_message, :sender_nickname]
|> Config.get(nil)
|> fetch_sender()
|> do_post(user, welcome_message())
end
defp do_post(%User{} = sender, recipient, message)
when is_binary(message) do
CommonAPI.post_chat_message(
sender,
recipient,
message
)
end
defp do_post(_sender, _recipient, _message), do: {:ok, nil}
defp fetch_sender(nickname) when is_binary(nickname) do
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
user
else
_ -> nil
end
end
defp fetch_sender(_), do: nil
defp welcome_message do
Config.get([:welcome, :chat_message, :message], nil)
end
end

View File

@ -21,8 +21,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do
@impl true
def describe, do: {:ok, %{}}
defp local?(%{"id" => id}) do
String.starts_with?(id, Pleroma.Web.Endpoint.url())
defp local?(%{"actor" => actor}) do
String.starts_with?(actor, Pleroma.Web.Endpoint.url())
end
defp note?(activity) do

View File

@ -37,8 +37,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
defp check_delist(message, actions) do
if :delist in actions do
with %User{} = user <- User.get_cached_by_ap_id(message["actor"]) do
to = List.delete(message["to"], Pleroma.Constants.as_public()) ++ [user.follower_address]
cc = List.delete(message["cc"], user.follower_address) ++ [Pleroma.Constants.as_public()]
to =
List.delete(message["to"] || [], Pleroma.Constants.as_public()) ++
[user.follower_address]
cc =
List.delete(message["cc"] || [], user.follower_address) ++
[Pleroma.Constants.as_public()]
message =
message
@ -58,8 +63,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
defp check_strip_followers(message, actions) do
if :strip_followers in actions do
with %User{} = user <- User.get_cached_by_ap_id(message["actor"]) do
to = List.delete(message["to"], user.follower_address)
cc = List.delete(message["cc"], user.follower_address)
to = List.delete(message["to"] || [], user.follower_address)
cc = List.delete(message["cc"] || [], user.follower_address)
message =
message

View File

@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
@behaviour Pleroma.Web.ActivityPub.MRF
alias Pleroma.Config
alias Pleroma.FollowingRelationship
alias Pleroma.User
alias Pleroma.Web.ActivityPub.MRF
@ -108,6 +109,35 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
{:ok, object}
end
defp intersection(list1, list2) do
list1 -- list1 -- list2
end
defp check_followers_only(%{host: actor_host} = _actor_info, object) do
followers_only =
Config.get([:mrf_simple, :followers_only])
|> MRF.subdomains_regex()
object =
with true <- MRF.subdomain_match?(followers_only, actor_host),
user <- User.get_cached_by_ap_id(object["actor"]) do
# Don't use Map.get/3 intentionally, these must not be nil
fixed_to = object["to"] || []
fixed_cc = object["cc"] || []
to = FollowingRelationship.followers_ap_ids(user, fixed_to)
cc = FollowingRelationship.followers_ap_ids(user, fixed_cc)
object
|> Map.put("to", intersection([user.follower_address | to], fixed_to))
|> Map.put("cc", intersection([user.follower_address | cc], fixed_cc))
else
_ -> object
end
{:ok, object}
end
defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do
report_removal =
Config.get([:mrf_simple, :report_removal])
@ -174,6 +204,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
{:ok, object} <- check_media_removal(actor_info, object),
{:ok, object} <- check_media_nsfw(actor_info, object),
{:ok, object} <- check_ftl_removal(actor_info, object),
{:ok, object} <- check_followers_only(actor_info, object),
{:ok, object} <- check_report_removal(actor_info, object) do
{:ok, object}
else

View File

@ -34,10 +34,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do
cng
|> validate_change(field_name, fn field_name, actor ->
if User.get_cached_by_ap_id(actor) do
[]
else
[{field_name, "can't find user"}]
case User.get_cached_by_ap_id(actor) do
%User{deactivated: true} ->
[{field_name, "user is deactivated"}]
%User{} ->
[]
_ ->
[{field_name, "can't find user"}]
end
end)
end

View File

@ -449,21 +449,32 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
}
end
# TODO: This is actually a token respone, but there's no oauth operation file yet.
# Note: this is a token response (if login succeeds!), but there's no oauth operation file yet.
defp create_response do
%Schema{
title: "AccountCreateResponse",
description: "Response schema for an account",
type: :object,
properties: %{
# The response when auto-login on create succeeds (token is issued):
token_type: %Schema{type: :string},
access_token: %Schema{type: :string},
refresh_token: %Schema{type: :string},
scope: %Schema{type: :string},
created_at: %Schema{type: :integer, format: :"date-time"},
me: %Schema{type: :string},
expires_in: %Schema{type: :integer}
expires_in: %Schema{type: :integer},
#
# The response when registration succeeds but auto-login fails (no token):
identifier: %Schema{type: :string},
message: %Schema{type: :string}
},
required: [],
# Note: example of successful registration with failed login response:
# example: %{
# "identifier" => "missing_confirmed_email",
# "message" => "You have been registered. Please check your email for further instructions."
# },
example: %{
"token_type" => "Bearer",
"access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk",

View File

@ -19,13 +19,46 @@ defmodule Pleroma.Web.ApiSpec.Schemas.ChatMessage do
content: %Schema{type: :string, nullable: true},
created_at: %Schema{type: :string, format: :"date-time"},
emojis: %Schema{type: :array},
attachment: %Schema{type: :object, nullable: true}
attachment: %Schema{type: :object, nullable: true},
card: %Schema{
type: :object,
nullable: true,
description: "Preview card for links included within status content",
required: [:url, :title, :description, :type],
properties: %{
type: %Schema{
type: :string,
enum: ["link", "photo", "video", "rich"],
description: "The type of the preview card"
},
provider_name: %Schema{
type: :string,
nullable: true,
description: "The provider of the original resource"
},
provider_url: %Schema{
type: :string,
format: :uri,
description: "A link to the provider of the original resource"
},
url: %Schema{type: :string, format: :uri, description: "Location of linked resource"},
image: %Schema{
type: :string,
nullable: true,
format: :uri,
description: "Preview thumbnail"
},
title: %Schema{type: :string, description: "Title of linked resource"},
description: %Schema{type: :string, description: "Description of preview"}
}
}
},
example: %{
"account_id" => "someflakeid",
"chat_id" => "1",
"content" => "hey you again",
"created_at" => "2020-04-21T15:06:45.000Z",
"card" => nil,
"emojis" => [
%{
"static_url" => "https://dontbulling.me/emoji/Firefox.gif",

View File

@ -27,8 +27,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
alias Pleroma.Web.MastodonAPI.MastodonAPI
alias Pleroma.Web.MastodonAPI.MastodonAPIController
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.OAuth.OAuthController
alias Pleroma.Web.OAuth.OAuthView
alias Pleroma.Web.OAuth.Token
alias Pleroma.Web.TwitterAPI.TwitterAPI
plug(Pleroma.Web.ApiSpec.CastAndValidate)
@ -100,11 +100,34 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
def create(%{assigns: %{app: app}, body_params: params} = conn, _params) do
with :ok <- validate_email_param(params),
:ok <- TwitterAPI.validate_captcha(app, params),
{:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
{:ok, token} <- Token.create_token(app, user, %{scopes: app.scopes}) do
{:ok, user} <- TwitterAPI.register_user(params),
{_, {:ok, token}} <-
{:login, OAuthController.login(user, app, app.scopes)} do
json(conn, OAuthView.render("token.json", %{user: user, token: token}))
else
{:error, error} -> json_response(conn, :bad_request, %{error: error})
{:login, {:account_status, :confirmation_pending}} ->
json_response(conn, :ok, %{
message: "You have been registered. Please check your email for further instructions.",
identifier: "missing_confirmed_email"
})
{:login, {:account_status, :approval_pending}} ->
json_response(conn, :ok, %{
message:
"You have been registered. You'll be able to log in once your account is approved.",
identifier: "awaiting_approval"
})
{:login, _} ->
json_response(conn, :ok, %{
message:
"You have been registered. Some post-registration steps may be pending. " <>
"Please log in manually.",
identifier: "manual_login_required"
})
{:error, error} ->
json_response(conn, :bad_request, %{error: error})
end
end

View File

@ -271,11 +271,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
) do
with {:ok, %User{} = user} <- Authenticator.get_user(conn),
{:ok, app} <- Token.Utils.fetch_app(conn),
{:account_status, :active} <- {:account_status, User.account_status(user)},
{:ok, scopes} <- validate_scopes(app, params),
{:ok, auth} <- Authorization.create_authorization(app, user, scopes),
{:mfa_required, _, _, false} <- {:mfa_required, user, auth, MFA.require?(user)},
{:ok, token} <- Token.exchange_token(app, auth) do
requested_scopes <- Scopes.fetch_scopes(params, app.scopes),
{:ok, token} <- login(user, app, requested_scopes) do
json(conn, OAuthView.render("token.json", %{user: user, token: token}))
else
error ->
@ -533,6 +530,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
end
end
defp do_create_authorization(conn, auth_attrs, user \\ nil)
defp do_create_authorization(
%Plug.Conn{} = conn,
%{
@ -542,19 +541,37 @@ defmodule Pleroma.Web.OAuth.OAuthController do
"redirect_uri" => redirect_uri
} = auth_attrs
},
user \\ nil
user
) do
with {_, {:ok, %User{} = user}} <-
{:get_user, (user && {:ok, user}) || Authenticator.get_user(conn)},
%App{} = app <- Repo.get_by(App, client_id: client_id),
true <- redirect_uri in String.split(app.redirect_uris),
{:ok, scopes} <- validate_scopes(app, auth_attrs),
{:account_status, :active} <- {:account_status, User.account_status(user)},
{:ok, auth} <- Authorization.create_authorization(app, user, scopes) do
requested_scopes <- Scopes.fetch_scopes(auth_attrs, app.scopes),
{:ok, auth} <- do_create_authorization(user, app, requested_scopes) do
{:ok, auth, user}
end
end
defp do_create_authorization(%User{} = user, %App{} = app, requested_scopes)
when is_list(requested_scopes) do
with {:account_status, :active} <- {:account_status, User.account_status(user)},
{:ok, scopes} <- validate_scopes(app, requested_scopes),
{:ok, auth} <- Authorization.create_authorization(app, user, scopes) do
{:ok, auth}
end
end
# Note: intended to be a private function but opened for AccountController that logs in on signup
@doc "If checks pass, creates authorization and token for given user, app and requested scopes."
def login(%User{} = user, %App{} = app, requested_scopes) when is_list(requested_scopes) do
with {:ok, auth} <- do_create_authorization(user, app, requested_scopes),
{:mfa_required, _, _, false} <- {:mfa_required, user, auth, MFA.require?(user)},
{:ok, token} <- Token.exchange_token(app, auth) do
{:ok, token}
end
end
# Special case: Local MastodonFE
defp redirect_uri(%Plug.Conn{} = conn, "."), do: auth_url(conn, :login)
@ -571,12 +588,15 @@ defmodule Pleroma.Web.OAuth.OAuthController do
end
end
@spec validate_scopes(App.t(), map()) ::
@spec validate_scopes(App.t(), map() | list()) ::
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
defp validate_scopes(%App{} = app, params) do
params
|> Scopes.fetch_scopes(app.scopes)
|> Scopes.validate(app.scopes)
defp validate_scopes(%App{} = app, params) when is_map(params) do
requested_scopes = Scopes.fetch_scopes(params, app.scopes)
validate_scopes(app, requested_scopes)
end
defp validate_scopes(%App{} = app, requested_scopes) when is_list(requested_scopes) do
Scopes.validate(requested_scopes, app.scopes)
end
def default_redirect_uri(%App{} = app) do

View File

@ -14,7 +14,7 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
%{
chat_message_reference: %{
id: id,
object: %{data: chat_message},
object: %{data: chat_message} = object,
chat_id: chat_id,
unread: unread
}
@ -30,7 +30,12 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
attachment:
chat_message["attachment"] &&
StatusView.render("attachment.json", attachment: chat_message["attachment"]),
unread: unread
unread: unread,
card:
StatusView.render(
"card.json",
Pleroma.Web.RichMedia.Helpers.fetch_data_for_object(object)
)
}
end

View File

@ -9,6 +9,11 @@ defmodule Pleroma.Web.RichMedia.Helpers do
alias Pleroma.Object
alias Pleroma.Web.RichMedia.Parser
@rich_media_options [
pool: :media,
max_body: 2_000_000
]
@spec validate_page_url(URI.t() | binary()) :: :ok | :error
defp validate_page_url(page_url) when is_binary(page_url) do
validate_tld = Pleroma.Config.get([Pleroma.Formatter, :validate_tld])
@ -49,11 +54,11 @@ defmodule Pleroma.Web.RichMedia.Helpers do
|> hd
end
def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do
def fetch_data_for_object(object) do
with true <- Config.get([:rich_media, :enabled]),
%Object{} = object <- Object.normalize(activity),
false <- object.data["sensitive"] || false,
{:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]),
{:ok, page_url} <-
HTML.extract_first_external_url(object, object.data["content"]),
:ok <- validate_page_url(page_url),
{:ok, rich_media} <- Parser.parse(page_url) do
%{page_url: page_url, rich_media: rich_media}
@ -62,10 +67,35 @@ defmodule Pleroma.Web.RichMedia.Helpers do
end
end
def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do
with true <- Config.get([:rich_media, :enabled]),
%Object{} = object <- Object.normalize(activity) do
fetch_data_for_object(object)
else
_ -> %{}
end
end
def fetch_data_for_activity(_), do: %{}
def perform(:fetch, %Activity{} = activity) do
fetch_data_for_activity(activity)
:ok
end
def rich_media_get(url) do
headers = [{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}]
options =
if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
Keyword.merge(@rich_media_options,
recv_timeout: 2_000,
with_body: true
)
else
@rich_media_options
end
Pleroma.HTTP.get(url, headers, options)
end
end

View File

@ -3,11 +3,6 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parser do
@options [
pool: :media,
max_body: 2_000_000
]
defp parsers do
Pleroma.Config.get([:rich_media, :parsers])
end
@ -75,21 +70,8 @@ defmodule Pleroma.Web.RichMedia.Parser do
end
defp parse_url(url) do
opts =
if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
Keyword.merge(@options,
recv_timeout: 2_000,
with_body: true
)
else
@options
end
try do
rich_media_agent = Pleroma.Application.user_agent() <> "; Bot"
{:ok, %Tesla.Env{body: html}} =
Pleroma.HTTP.get(url, [{"user-agent", rich_media_agent}], adapter: opts)
{:ok, %Tesla.Env{body: html}} = Pleroma.Web.RichMedia.Helpers.rich_media_get(url)
html
|> parse_html()

View File

@ -22,7 +22,7 @@ defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
end
defp get_oembed_data(url) do
with {:ok, %Tesla.Env{body: json}} <- Pleroma.HTTP.get(url, [], adapter: [pool: :media]) do
with {:ok, %Tesla.Env{body: json}} <- Pleroma.Web.RichMedia.Helpers.rich_media_get(url) do
Jason.decode(json)
end
end

View File

@ -178,7 +178,7 @@ defmodule Pleroma.Mixfile do
{:flake_id, "~> 0.1.0"},
{:concurrent_limiter,
git: "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter.git",
ref: "8eee96c6ba39b9286ec44c51c52d9f2758951365"},
ref: "55e92f84b4ed531bd487952a71040a9c69dc2807"},
{:remote_ip,
git: "https://git.pleroma.social/pleroma/remote_ip.git",
ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"},
@ -214,7 +214,8 @@ defmodule Pleroma.Mixfile do
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate", "test"],
docs: ["pleroma.docs", "docs"]
docs: ["pleroma.docs", "docs"],
analyze: ["credo --strict --only=warnings,todo,fixme,consistency,readability"]
]
end

View File

@ -14,7 +14,7 @@
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"comeonin": {:hex, :comeonin, "5.3.1", "7fe612b739c78c9c1a75186ef2d322ce4d25032d119823269d0aa1e2f1e20025", [:mix], [], "hexpm", "d6222483060c17f0977fad1b7401ef0c5863c985a64352755f366aee3799c245"},
"concurrent_limiter": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter.git", "8eee96c6ba39b9286ec44c51c52d9f2758951365", [ref: "8eee96c6ba39b9286ec44c51c52d9f2758951365"]},
"concurrent_limiter": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter.git", "55e92f84b4ed531bd487952a71040a9c69dc2807", [ref: "55e92f84b4ed531bd487952a71040a9c69dc2807"]},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
"cors_plug": {:hex, :cors_plug, "2.0.2", "2b46083af45e4bc79632bd951550509395935d3e7973275b2b743bd63cc942ce", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f0d0e13f71c51fd4ef8b2c7e051388e4dfb267522a83a22392c856de7e46465f"},
"cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"},

View File

@ -0,0 +1,37 @@
# Fix legacy tags set by AdminFE that don't align with TagPolicy MRF
defmodule Pleroma.Repo.Migrations.FixLegacyTags do
use Ecto.Migration
alias Pleroma.Repo
alias Pleroma.User
import Ecto.Query
@old_new_map %{
"force_nsfw" => "mrf_tag:media-force-nsfw",
"strip_media" => "mrf_tag:media-strip",
"force_unlisted" => "mrf_tag:force-unlisted",
"sandbox" => "mrf_tag:sandbox",
"disable_remote_subscription" => "mrf_tag:disable-remote-subscription",
"disable_any_subscription" => "mrf_tag:disable-any-subscription"
}
def change do
legacy_tags = Map.keys(@old_new_map)
from(u in User, where: fragment("? && ?", u.tags, ^legacy_tags))
|> Repo.all()
|> Enum.each(fn user ->
fix_tags_changeset(user)
|> Repo.update()
end)
end
defp fix_tags_changeset(%User{tags: tags} = user) do
new_tags =
Enum.map(tags, fn tag ->
Map.get(@old_new_map, tag, tag)
end)
Ecto.Changeset.change(user, tags: new_tags)
end
end

View File

@ -0,0 +1,19 @@
defmodule Pleroma.Repo.Migrations.RemoveNonlocalExpirations do
use Ecto.Migration
def up do
statement = """
DELETE FROM
activity_expirations A USING activities B
WHERE
A.activity_id = B.id
AND B.local = false;
"""
execute(statement)
end
def down do
:ok
end
end

View File

@ -0,0 +1,7 @@
defmodule Pleroma.Repo.Migrations.AddUniqueIndexToAppClientId do
use Ecto.Migration
def change do
create(unique_index(:apps, [:client_id]))
end
end

View File

@ -28,6 +28,34 @@ defmodule Pleroma.ConfigTest do
assert Pleroma.Config.get([:azerty, :uiop], true) == true
end
describe "nil values" do
setup do
Pleroma.Config.put(:lorem, nil)
Pleroma.Config.put(:ipsum, %{dolor: [sit: nil]})
Pleroma.Config.put(:dolor, sit: %{amet: nil})
on_exit(fn -> Enum.each(~w(lorem ipsum dolor)a, &Pleroma.Config.delete/1) end)
end
test "get/1 with an atom for nil value" do
assert Pleroma.Config.get(:lorem) == nil
end
test "get/2 with an atom for nil value" do
assert Pleroma.Config.get(:lorem, true) == nil
end
test "get/1 with a list of keys for nil value" do
assert Pleroma.Config.get([:ipsum, :dolor, :sit]) == nil
assert Pleroma.Config.get([:dolor, :sit, :amet]) == nil
end
test "get/2 with a list of keys for nil value" do
assert Pleroma.Config.get([:ipsum, :dolor, :sit], true) == nil
assert Pleroma.Config.get([:dolor, :sit, :amet], true) == nil
end
end
test "get/1 when value is false" do
Pleroma.Config.put([:instance, :false_test], false)
Pleroma.Config.put([:instance, :nested], [])
@ -89,5 +117,23 @@ defmodule Pleroma.ConfigTest do
Pleroma.Config.put([:delete_me, :delete_me], hello: "world", world: "Hello")
Pleroma.Config.delete([:delete_me, :delete_me, :world])
assert Pleroma.Config.get([:delete_me, :delete_me]) == [hello: "world"]
assert Pleroma.Config.delete([:this_key_does_not_exist])
assert Pleroma.Config.delete([:non, :existing, :key])
end
test "fetch/1" do
Pleroma.Config.put([:lorem], :ipsum)
Pleroma.Config.put([:ipsum], dolor: :sit)
assert Pleroma.Config.fetch([:lorem]) == {:ok, :ipsum}
assert Pleroma.Config.fetch(:lorem) == {:ok, :ipsum}
assert Pleroma.Config.fetch([:ipsum, :dolor]) == {:ok, :sit}
assert Pleroma.Config.fetch([:lorem, :ipsum]) == :error
assert Pleroma.Config.fetch([:loremipsum]) == :error
assert Pleroma.Config.fetch(:loremipsum) == :error
Pleroma.Config.delete([:lorem])
Pleroma.Config.delete([:ipsum])
end
end

View File

@ -0,0 +1,24 @@
defmodule Pleroma.Repo.Migrations.FixLegacyTagsTest do
alias Pleroma.User
use Pleroma.DataCase
import Pleroma.Factory
import Pleroma.Tests.Helpers
setup_all do: require_migration("20200802170532_fix_legacy_tags")
test "change/0 converts legacy user tags into correct values", %{migration: migration} do
user = insert(:user, tags: ["force_nsfw", "force_unlisted", "verified"])
user2 = insert(:user)
assert :ok == migration.change()
fixed_user = User.get_by_id(user.id)
fixed_user2 = User.get_by_id(user2.id)
assert fixed_user.tags == ["mrf_tag:media-force-nsfw", "mrf_tag:force-unlisted", "verified"]
assert fixed_user2.tags == []
# user2 should not have been updated
assert fixed_user2.updated_at == fixed_user2.inserted_at
end
end

16
test/report_note_test.exs Normal file
View File

@ -0,0 +1,16 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReportNoteTest do
alias Pleroma.ReportNote
use Pleroma.DataCase
import Pleroma.Factory
test "create/3" do
user = insert(:user)
report = insert(:report_activity)
assert {:ok, note} = ReportNote.create(user.id, report.id, "naughty boy")
assert note.content == "naughty boy"
end
end

View File

@ -297,6 +297,30 @@ defmodule Pleroma.Factory do
}
end
def report_activity_factory(attrs \\ %{}) do
user = attrs[:user] || insert(:user)
activity = attrs[:activity] || insert(:note_activity)
state = attrs[:state] || "open"
data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"actor" => user.ap_id,
"type" => "Flag",
"object" => [activity.actor, activity.data["id"]],
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
"to" => [],
"cc" => [activity.actor],
"context" => activity.data["context"],
"state" => state
}
%Pleroma.Activity{
data: data,
actor: data["actor"],
recipients: data["to"] ++ data["cc"]
}
end
def oauth_app_factory do
%Pleroma.Web.OAuth.App{
client_name: sequence(:client_name, &"Some client #{&1}"),

View File

@ -17,9 +17,19 @@ defmodule Pleroma.Tests.Helpers do
defmacro clear_config(config_path, do: yield) do
quote do
initial_setting = Config.get(unquote(config_path))
initial_setting = Config.fetch(unquote(config_path))
unquote(yield)
on_exit(fn -> Config.put(unquote(config_path), initial_setting) end)
on_exit(fn ->
case initial_setting do
:error ->
Config.delete(unquote(config_path))
{:ok, value} ->
Config.put(unquote(config_path), value)
end
end)
:ok
end
end

View File

@ -50,13 +50,13 @@ defmodule Mix.Tasks.Pleroma.AppTest do
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_receive {:mix_shell, :info, [message]}
assert message == "#{name} successfully created:"
assert_received {:mix_shell, :info, [message]}
assert_receive {:mix_shell, :info, [message]}
assert message == "App client_id: #{app.client_id}"
assert_received {:mix_shell, :info, [message]}
assert_receive {:mix_shell, :info, [message]}
assert message == "App client_secret: #{app.client_secret}"
assert app.scopes == scopes

View File

@ -0,0 +1,35 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.WelcomeChatMessageTest do
use Pleroma.DataCase
alias Pleroma.Config
alias Pleroma.User.WelcomeChatMessage
import Pleroma.Factory
setup do: clear_config([:welcome])
describe "post_message/1" do
test "send a chat welcome message" do
welcome_user = insert(:user, name: "mewmew")
user = insert(:user)
Config.put([:welcome, :chat_message, :enabled], true)
Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname)
Config.put(
[:welcome, :chat_message, :message],
"Hello, welcome to Blob/Cat!"
)
{:ok, %Pleroma.Activity{} = activity} = WelcomeChatMessage.post_message(user)
assert user.ap_id in activity.recipients
assert Pleroma.Object.normalize(activity).data["type"] == "ChatMessage"
assert Pleroma.Object.normalize(activity).data["content"] == "Hello, welcome to Blob/Cat!"
end
end
end

View File

@ -412,8 +412,36 @@ defmodule Pleroma.UserTest do
welcome_user = insert(:user)
Pleroma.Config.put([:welcome, :direct_message, :enabled], true)
Pleroma.Config.put([:welcome, :direct_message, :sender_nickname], welcome_user.nickname)
Pleroma.Config.put([:welcome, :direct_message, :message], "Hello, this is a cool site")
Pleroma.Config.put([:welcome, :direct_message, :message], "Hello, this is a direct message")
cng = User.register_changeset(%User{}, @full_user_data)
{:ok, registered_user} = User.register(cng)
ObanHelpers.perform_all()
activity = Repo.one(Pleroma.Activity)
assert registered_user.ap_id in activity.recipients
assert Object.normalize(activity).data["content"] =~ "direct message"
assert activity.actor == welcome_user.ap_id
end
test "it sends a welcome chat message if it is set" do
welcome_user = insert(:user)
Pleroma.Config.put([:welcome, :chat_message, :enabled], true)
Pleroma.Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname)
Pleroma.Config.put([:welcome, :chat_message, :message], "Hello, this is a chat message")
cng = User.register_changeset(%User{}, @full_user_data)
{:ok, registered_user} = User.register(cng)
ObanHelpers.perform_all()
activity = Repo.one(Pleroma.Activity)
assert registered_user.ap_id in activity.recipients
assert Object.normalize(activity).data["content"] =~ "chat message"
assert activity.actor == welcome_user.ap_id
end
test "it sends a welcome email message if it is set" do
welcome_user = insert(:user)
Pleroma.Config.put([:welcome, :email, :enabled], true)
Pleroma.Config.put([:welcome, :email, :sender], welcome_user.email)
@ -428,11 +456,6 @@ defmodule Pleroma.UserTest do
{:ok, registered_user} = User.register(cng)
ObanHelpers.perform_all()
activity = Repo.one(Pleroma.Activity)
assert registered_user.ap_id in activity.recipients
assert Object.normalize(activity).data["content"] =~ "cool site"
assert activity.actor == welcome_user.ap_id
assert_email_sent(
from: {instance_name, welcome_user.email},
to: {registered_user.name, registered_user.email},
@ -490,6 +513,29 @@ defmodule Pleroma.UserTest do
refute changeset.valid?
end
test "it blocks blacklisted email domains" do
clear_config([User, :email_blacklist], ["trolling.world"])
# Block with match
params = Map.put(@full_user_data, :email, "troll@trolling.world")
changeset = User.register_changeset(%User{}, params)
refute changeset.valid?
# Block with subdomain match
params = Map.put(@full_user_data, :email, "troll@gnomes.trolling.world")
changeset = User.register_changeset(%User{}, params)
refute changeset.valid?
# Pass with different domains that are similar
params = Map.put(@full_user_data, :email, "troll@gnomestrolling.world")
changeset = User.register_changeset(%User{}, params)
assert changeset.valid?
params = Map.put(@full_user_data, :email, "troll@trolling.world.us")
changeset = User.register_changeset(%User{}, params)
assert changeset.valid?
end
test "it sets the password_hash and ap_id" do
changeset = User.register_changeset(%User{}, @full_user_data)
@ -500,6 +546,24 @@ defmodule Pleroma.UserTest do
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
end
test "it sets the 'accepts_chat_messages' set to true" do
changeset = User.register_changeset(%User{}, @full_user_data)
assert changeset.valid?
{:ok, user} = Repo.insert(changeset)
assert user.accepts_chat_messages
end
test "it creates a confirmed user" do
changeset = User.register_changeset(%User{}, @full_user_data)
assert changeset.valid?
{:ok, user} = Repo.insert(changeset)
refute user.confirmation_pending
end
end
describe "user registration, with :account_activation_required" do
@ -513,15 +577,6 @@ defmodule Pleroma.UserTest do
}
setup do: clear_config([:instance, :account_activation_required], true)
test "it sets the 'accepts_chat_messages' set to true" do
changeset = User.register_changeset(%User{}, @full_user_data)
assert changeset.valid?
{:ok, user} = Repo.insert(changeset)
assert user.accepts_chat_messages
end
test "it creates unconfirmed user" do
changeset = User.register_changeset(%User{}, @full_user_data)
assert changeset.valid?

View File

@ -7,11 +7,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do
alias Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy
@id Pleroma.Web.Endpoint.url() <> "/activities/cofe"
@local_actor Pleroma.Web.Endpoint.url() <> "/users/cofe"
test "adds `expires_at` property" do
assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} =
ActivityExpirationPolicy.filter(%{
"id" => @id,
"actor" => @local_actor,
"type" => "Create",
"object" => %{"type" => "Note"}
})
@ -25,6 +27,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do
assert {:ok, %{"type" => "Create", "expires_at" => ^expires_at}} =
ActivityExpirationPolicy.filter(%{
"id" => @id,
"actor" => @local_actor,
"type" => "Create",
"expires_at" => expires_at,
"object" => %{"type" => "Note"}
@ -37,6 +40,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do
assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} =
ActivityExpirationPolicy.filter(%{
"id" => @id,
"actor" => @local_actor,
"type" => "Create",
"expires_at" => too_distant_future,
"object" => %{"type" => "Note"}
@ -49,6 +53,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do
assert {:ok, activity} =
ActivityExpirationPolicy.filter(%{
"id" => "https://example.com/123",
"actor" => "https://example.com/users/cofe",
"type" => "Create",
"object" => %{"type" => "Note"}
})
@ -60,6 +65,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do
assert {:ok, activity} =
ActivityExpirationPolicy.filter(%{
"id" => "https://example.com/123",
"actor" => "https://example.com/users/cofe",
"type" => "Follow"
})
@ -68,6 +74,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do
assert {:ok, activity} =
ActivityExpirationPolicy.filter(%{
"id" => "https://example.com/123",
"actor" => "https://example.com/users/cofe",
"type" => "Create",
"object" => %{"type" => "Cofe"}
})

View File

@ -38,6 +38,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do
end
describe "with reject action" do
test "works with objects with empty to or cc fields" do
Config.put([:mrf_object_age, :actions], [:reject])
data =
get_old_message()
|> Map.put("cc", nil)
|> Map.put("to", nil)
assert match?({:reject, _}, ObjectAgePolicy.filter(data))
end
test "it rejects an old post" do
Config.put([:mrf_object_age, :actions], [:reject])
@ -56,6 +67,21 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do
end
describe "with delist action" do
test "works with objects with empty to or cc fields" do
Config.put([:mrf_object_age, :actions], [:delist])
data =
get_old_message()
|> Map.put("cc", nil)
|> Map.put("to", nil)
{:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"])
{:ok, data} = ObjectAgePolicy.filter(data)
assert Visibility.get_visibility(%{data: data}) == "unlisted"
end
test "it delists an old post" do
Config.put([:mrf_object_age, :actions], [:delist])
@ -80,6 +106,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do
end
describe "with strip_followers action" do
test "works with objects with empty to or cc fields" do
Config.put([:mrf_object_age, :actions], [:strip_followers])
data =
get_old_message()
|> Map.put("cc", nil)
|> Map.put("to", nil)
{:ok, user} = User.get_or_fetch_by_ap_id(data["actor"])
{:ok, data} = ObjectAgePolicy.filter(data)
refute user.follower_address in data["to"]
refute user.follower_address in data["cc"]
end
test "it strips followers collections from an old post" do
Config.put([:mrf_object_age, :actions], [:strip_followers])

View File

@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
import Pleroma.Factory
alias Pleroma.Config
alias Pleroma.Web.ActivityPub.MRF.SimplePolicy
alias Pleroma.Web.CommonAPI
setup do:
clear_config(:mrf_simple,
@ -15,6 +16,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
federated_timeline_removal: [],
report_removal: [],
reject: [],
followers_only: [],
accept: [],
avatar_removal: [],
banner_removal: [],
@ -261,6 +263,64 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
end
end
describe "when :followers_only" do
test "is empty" do
Config.put([:mrf_simple, :followers_only], [])
{_, ftl_message} = build_ftl_actor_and_message()
local_message = build_local_message()
assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message}
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "has a matching host" do
actor = insert(:user)
following_user = insert(:user)
non_following_user = insert(:user)
{:ok, _, _, _} = CommonAPI.follow(following_user, actor)
activity = %{
"actor" => actor.ap_id,
"to" => [
"https://www.w3.org/ns/activitystreams#Public",
following_user.ap_id,
non_following_user.ap_id
],
"cc" => [actor.follower_address, "http://foo.bar/qux"]
}
dm_activity = %{
"actor" => actor.ap_id,
"to" => [
following_user.ap_id,
non_following_user.ap_id
],
"cc" => []
}
actor_domain =
activity
|> Map.fetch!("actor")
|> URI.parse()
|> Map.fetch!(:host)
Config.put([:mrf_simple, :followers_only], [actor_domain])
assert {:ok, new_activity} = SimplePolicy.filter(activity)
assert actor.follower_address in new_activity["cc"]
assert following_user.ap_id in new_activity["to"]
refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["to"]
refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["cc"]
refute non_following_user.ap_id in new_activity["to"]
refute non_following_user.ap_id in new_activity["cc"]
assert {:ok, new_dm_activity} = SimplePolicy.filter(dm_activity)
assert new_dm_activity["to"] == [following_user.ap_id]
assert new_dm_activity["cc"] == []
end
end
describe "when :accept" do
test "is empty" do
Config.put([:mrf_simple, :accept], [])

View File

@ -124,6 +124,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.ChatMessageTest do
{:ok, %Activity{} = _activity} = Transmogrifier.handle_incoming(data)
end
test "it doesn't work for deactivated users" do
data =
File.read!("test/fixtures/create-chat-message.json")
|> Poison.decode!()
_author =
insert(:user,
ap_id: data["actor"],
local: false,
last_refreshed_at: DateTime.utc_now(),
deactivated: true
)
_recipient = insert(:user, ap_id: List.first(data["to"]), local: true)
assert {:error, _} = Transmogrifier.handle_incoming(data)
end
test "it inserts it and creates a chat" do
data =
File.read!("test/fixtures/create-chat-message.json")

View File

@ -163,6 +163,14 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil"
end
test "it does not work for deactivated users" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
insert(:user, ap_id: data["actor"], deactivated: true)
assert {:error, _} = Transmogrifier.handle_incoming(data)
end
test "it works for incoming notices" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()

View File

@ -458,6 +458,11 @@ defmodule Pleroma.Web.CommonAPITest do
end
describe "posting" do
test "deactivated users can't post" do
user = insert(:user, deactivated: true)
assert {:error, _} = CommonAPI.post(user, %{status: "ye"})
end
test "it supports explicit addressing" do
user = insert(:user)
user_two = insert(:user)

View File

@ -5,7 +5,6 @@
defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
use Pleroma.Web.ConnCase
alias Pleroma.Config
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
@ -16,8 +15,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
import Pleroma.Factory
describe "account fetching" do
setup do: clear_config([:instance, :limit_to_local_content])
test "works by id" do
%User{id: user_id} = insert(:user)
@ -42,7 +39,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
end
test "works by nickname for remote users" do
Config.put([:instance, :limit_to_local_content], false)
clear_config([:instance, :limit_to_local_content], false)
user = insert(:user, nickname: "user@example.com", local: false)
@ -53,7 +50,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
end
test "respects limit_to_local_content == :all for remote user nicknames" do
Config.put([:instance, :limit_to_local_content], :all)
clear_config([:instance, :limit_to_local_content], :all)
user = insert(:user, nickname: "user@example.com", local: false)
@ -63,7 +60,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
end
test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
Config.put([:instance, :limit_to_local_content], :unauthenticated)
clear_config([:instance, :limit_to_local_content], :unauthenticated)
user = insert(:user, nickname: "user@example.com", local: false)
reading_user = insert(:user)
@ -903,10 +900,93 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
[valid_params: valid_params]
end
setup do: clear_config([:instance, :account_activation_required])
setup do: clear_config([:instance, :account_approval_required])
test "registers and logs in without :account_activation_required / :account_approval_required",
%{conn: conn} do
clear_config([:instance, :account_activation_required], false)
clear_config([:instance, :account_approval_required], false)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/apps", %{
client_name: "client_name",
redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
scopes: "read, write, follow"
})
assert %{
"client_id" => client_id,
"client_secret" => client_secret,
"id" => _,
"name" => "client_name",
"redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
"vapid_key" => _,
"website" => nil
} = json_response_and_validate_schema(conn, 200)
conn =
post(conn, "/oauth/token", %{
grant_type: "client_credentials",
client_id: client_id,
client_secret: client_secret
})
assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
json_response(conn, 200)
assert token
token_from_db = Repo.get_by(Token, token: token)
assert token_from_db
assert refresh
assert scope == "read write follow"
clear_config([User, :email_blacklist], ["example.org"])
params = %{
username: "lain",
email: "lain@example.org",
password: "PlzDontHackLain",
bio: "Test Bio",
agreement: true
}
conn =
build_conn()
|> put_req_header("content-type", "multipart/form-data")
|> put_req_header("authorization", "Bearer " <> token)
|> post("/api/v1/accounts", params)
assert %{"error" => "{\"email\":[\"Invalid email\"]}"} =
json_response_and_validate_schema(conn, 400)
Pleroma.Config.put([User, :email_blacklist], [])
conn =
build_conn()
|> put_req_header("content-type", "multipart/form-data")
|> put_req_header("authorization", "Bearer " <> token)
|> post("/api/v1/accounts", params)
%{
"access_token" => token,
"created_at" => _created_at,
"scope" => ^scope,
"token_type" => "Bearer"
} = json_response_and_validate_schema(conn, 200)
token_from_db = Repo.get_by(Token, token: token)
assert token_from_db
user = Repo.preload(token_from_db, :user).user
assert user
refute user.confirmation_pending
refute user.approval_pending
end
test "registers but does not log in with :account_activation_required", %{conn: conn} do
clear_config([:instance, :account_activation_required], true)
clear_config([:instance, :account_approval_required], false)
test "Account registration via Application", %{conn: conn} do
conn =
conn
|> put_req_header("content-type", "application/json")
@ -954,23 +1034,18 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
agreement: true
})
%{
"access_token" => token,
"created_at" => _created_at,
"scope" => ^scope,
"token_type" => "Bearer"
} = json_response_and_validate_schema(conn, 200)
response = json_response_and_validate_schema(conn, 200)
assert %{"identifier" => "missing_confirmed_email"} = response
refute response["access_token"]
refute response["token_type"]
token_from_db = Repo.get_by(Token, token: token)
assert token_from_db
token_from_db = Repo.preload(token_from_db, :user)
assert token_from_db.user
assert token_from_db.user.confirmation_pending
user = Repo.get_by(User, email: "lain@example.org")
assert user.confirmation_pending
end
test "Account registration via app with account_approval_required", %{conn: conn} do
Pleroma.Config.put([:instance, :account_approval_required], true)
test "registers but does not log in with :account_approval_required", %{conn: conn} do
clear_config([:instance, :account_approval_required], true)
clear_config([:instance, :account_activation_required], false)
conn =
conn
@ -1020,22 +1095,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
reason: "I'm a cool dude, bro"
})
%{
"access_token" => token,
"created_at" => _created_at,
"scope" => ^scope,
"token_type" => "Bearer"
} = json_response_and_validate_schema(conn, 200)
response = json_response_and_validate_schema(conn, 200)
assert %{"identifier" => "awaiting_approval"} = response
refute response["access_token"]
refute response["token_type"]
token_from_db = Repo.get_by(Token, token: token)
assert token_from_db
token_from_db = Repo.preload(token_from_db, :user)
assert token_from_db.user
user = Repo.get_by(User, email: "lain@example.org")
assert token_from_db.user.confirmation_pending
assert token_from_db.user.approval_pending
assert token_from_db.user.registration_reason == "I'm a cool dude, bro"
assert user.approval_pending
assert user.registration_reason == "I'm a cool dude, bro"
end
test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
@ -1089,11 +1157,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
end)
end
setup do: clear_config([:instance, :account_activation_required])
test "returns bad_request if missing email params when :account_activation_required is enabled",
%{conn: conn, valid_params: valid_params} do
Pleroma.Config.put([:instance, :account_activation_required], true)
clear_config([:instance, :account_activation_required], true)
app_token = insert(:oauth_token, user: nil)
@ -1258,8 +1324,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
assert token_from_db
token_from_db = Repo.preload(token_from_db, :user)
assert token_from_db.user
assert token_from_db.user.confirmation_pending
end
conn =

View File

@ -17,8 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do
test "returns error when followed user is deactivated" do
follower = insert(:user)
user = insert(:user, local: true, deactivated: true)
{:error, error} = MastodonAPI.follow(follower, user)
assert error == :rejected
assert {:error, _error} = MastodonAPI.follow(follower, user)
end
test "following for user" do

View File

@ -29,5 +29,16 @@ defmodule Pleroma.Web.OAuth.AppTest do
assert exist_app.id == app.id
assert exist_app.scopes == ["read", "write", "follow", "push"]
end
test "has unique client_id" do
insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop")
error =
catch_error(insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop"))
assert %Ecto.ConstraintError{} = error
assert error.constraint == "apps_client_id_index"
assert error.type == :unique
end
end
end

View File

@ -43,7 +43,17 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceViewTest do
assert chat_message[:unread] == false
assert match?([%{shortcode: "firefox"}], chat_message[:emojis])
{:ok, activity} = CommonAPI.post_chat_message(recipient, user, "gkgkgk", media_id: upload.id)
clear_config([:rich_media, :enabled], true)
Tesla.Mock.mock(fn
%{url: "https://example.com/ogp"} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
end)
{:ok, activity} =
CommonAPI.post_chat_message(recipient, user, "gkgkgk https://example.com/ogp",
media_id: upload.id
)
object = Object.normalize(activity)
@ -52,10 +62,11 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceViewTest do
chat_message_two = MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
assert chat_message_two[:id] == cm_ref.id
assert chat_message_two[:content] == "gkgkgk"
assert chat_message_two[:content] == object.data["content"]
assert chat_message_two[:account_id] == recipient.id
assert chat_message_two[:chat_id] == chat_message[:chat_id]
assert chat_message_two[:attachment]
assert chat_message_two[:unread] == true
assert chat_message_two[:card]
end
end