Merge branch 'develop' into issue/1383

This commit is contained in:
Maksim Pechnikov 2020-02-03 21:42:36 +03:00
commit 2c40c8b4a2
49 changed files with 773 additions and 97 deletions

View file

@ -12,8 +12,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed ### Changed
- **Breaking:** Pleroma won't start if it detects unapplied migrations - **Breaking:** Pleroma won't start if it detects unapplied migrations
- **Breaking:** attachments are removed along with statuses when there are no other references to it - **Breaking:** attachments are removed along with statuses. Does not affect duplicate files and attachments without status.
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7) - **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
- **Breaking:** `Pleroma.Plugs.RemoteIp` and `:rate_limiter` enabled by default. Please ensure your reverse proxy forwards the real IP!
- **Breaking:** attachment links (`config :pleroma, :instance, no_attachment_links` and `config :pleroma, Pleroma.Upload, link_name`) disabled by default - **Breaking:** attachment links (`config :pleroma, :instance, no_attachment_links` and `config :pleroma, Pleroma.Upload, link_name`) disabled by default
- **Breaking:** OAuth: defaulted `[:auth, :enforce_oauth_admin_scope_usage]` setting to `true` which demands `admin` OAuth scope to perform admin actions (in addition to `is_admin` flag on User); make sure to use bundled or newer versions of AdminFE & PleromaFE to access admin / moderator features. - **Breaking:** OAuth: defaulted `[:auth, :enforce_oauth_admin_scope_usage]` setting to `true` which demands `admin` OAuth scope to perform admin actions (in addition to `is_admin` flag on User); make sure to use bundled or newer versions of AdminFE & PleromaFE to access admin / moderator features.
- **Breaking:** Dynamic configuration has been rearchitected. The `:pleroma, :instance, dynamic_configuration` setting has been replaced with `config :pleroma, configurable_from_database`. Please backup your configuration to a file and run the migration task to ensure consistency with the new schema. - **Breaking:** Dynamic configuration has been rearchitected. The `:pleroma, :instance, dynamic_configuration` setting has been replaced with `config :pleroma, configurable_from_database`. Please backup your configuration to a file and run the migration task to ensure consistency with the new schema.
@ -27,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Deprecated `User.Info` embedded schema (fields moved to `User`) - Deprecated `User.Info` embedded schema (fields moved to `User`)
- Store status data inside Flag activity - Store status data inside Flag activity
- Deprecated (reorganized as `UserRelationship` entity) User fields with user AP IDs (`blocks`, `mutes`, `muted_reblogs`, `muted_notifications`, `subscribers`). - Deprecated (reorganized as `UserRelationship` entity) User fields with user AP IDs (`blocks`, `mutes`, `muted_reblogs`, `muted_notifications`, `subscribers`).
- Rate limiter is now disabled for localhost/socket (unless remoteip plug is enabled)
- Logger: default log level changed from `warn` to `info`. - Logger: default log level changed from `warn` to `info`.
- Config mix task `migrate_to_db` truncates `config` table before migrating the config file. - Config mix task `migrate_to_db` truncates `config` table before migrating the config file.
<details> <details>
@ -104,6 +106,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Mastodon API: Change emoji reaction reply format once more - Mastodon API: Change emoji reaction reply format once more
- Configuration: `feed.logo` option for tag feed. - Configuration: `feed.logo` option for tag feed.
- Tag feed: `/tags/:tag.rss` - list public statuses by hashtag. - Tag feed: `/tags/:tag.rss` - list public statuses by hashtag.
- Mastodon API: Add `reacted` property to `emoji_reactions`
</details> </details>
### Fixed ### Fixed

View file

@ -586,11 +586,21 @@
config :http_signatures, config :http_signatures,
adapter: Pleroma.Signature adapter: Pleroma.Signature
config :pleroma, :rate_limit, authentication: {60_000, 15} config :pleroma, :rate_limit,
authentication: {60_000, 15},
search: [{1000, 10}, {1000, 30}],
app_account_creation: {1_800_000, 25},
relations_actions: {10_000, 10},
relation_id_action: {60_000, 2},
statuses_actions: {10_000, 15},
status_id_action: {60_000, 3},
password_reset: {1_800_000, 5},
account_confirmation_resend: {8_640_000, 5},
ap_routes: {60_000, 15}
config :pleroma, Pleroma.ActivityExpiration, enabled: true config :pleroma, Pleroma.ActivityExpiration, enabled: true
config :pleroma, Pleroma.Plugs.RemoteIp, enabled: false config :pleroma, Pleroma.Plugs.RemoteIp, enabled: true
config :pleroma, :static_fe, enabled: false config :pleroma, :static_fe, enabled: false

View file

@ -29,7 +29,7 @@ Has these additional fields under the `pleroma` object:
- `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain` - `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
- `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire - `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire
- `thread_muted`: true if the thread the post belongs to is muted - `thread_muted`: true if the thread the post belongs to is muted
- `emoji_reactions`: A list with emoji / reaction maps. The format is {emoji: "☕", count: 1}. Contains no information about the reacting users, for that use the `emoji_reactions_by` endpoint. - `emoji_reactions`: A list with emoji / reaction maps. The format is `{emoji: "☕", count: 1, reacted: true}`. Contains no information about the reacting users, for that use the `emoji_reactions_by` endpoint.
## Attachments ## Attachments

View file

@ -455,7 +455,7 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
* Example Response: * Example Response:
```json ```json
[ [
{"emoji": "😀", "count": 2, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]}, {"emoji": "😀", "count": 2, "reacted": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]},
{"emoji": "☕", "count": 1, "accounts": [{"id" => "abc..."}]} {"emoji": "☕", "count": 1, "reacted": false, "accounts": [{"id" => "abc..."}]}
] ]
``` ```

View file

@ -308,16 +308,15 @@ This will make Pleroma listen on `127.0.0.1` port `8080` and generate urls start
Available options: Available options:
* `enabled` - Enable/disable the plug. Defaults to `false`. * `enabled` - Enable/disable the plug. Defaults to `false`.
* `headers` - A list of strings naming the `req_headers` to use when deriving the `remote_ip`. Order does not matter. Defaults to `~w[forwarded x-forwarded-for x-client-ip x-real-ip]`. * `headers` - A list of strings naming the `req_headers` to use when deriving the `remote_ip`. Order does not matter. Defaults to `["x-forwarded-for"]`.
* `proxies` - A list of strings in [CIDR](https://en.wikipedia.org/wiki/CIDR) notation specifying the IPs of known proxies. Defaults to `[]`. * `proxies` - A list of strings in [CIDR](https://en.wikipedia.org/wiki/CIDR) notation specifying the IPs of known proxies. Defaults to `[]`.
* `reserved` - Defaults to [localhost](https://en.wikipedia.org/wiki/Localhost) and [private network](https://en.wikipedia.org/wiki/Private_network). * `reserved` - Defaults to [localhost](https://en.wikipedia.org/wiki/Localhost) and [private network](https://en.wikipedia.org/wiki/Private_network).
### :rate_limit ### :rate_limit
This is an advanced feature and disabled by default. !!! note
If your instance is behind a reverse proxy ensure [`Pleroma.Plugs.RemoteIp`](#pleroma-plugs-remoteip) is enabled (it is enabled by default).
If your instance is behind a reverse proxy you must enable and configure [`Pleroma.Plugs.RemoteIp`](#pleroma-plugs-remoteip).
A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where: A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where:
@ -326,14 +325,31 @@ A keyword list of rate limiters where a key is a limiter name and value is the l
It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated. It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated.
For example:
```elixir
config :pleroma, :rate_limit,
authentication: {60_000, 15},
search: [{1000, 10}, {1000, 30}]
```
Means that:
1. In 60 seconds, 15 authentication attempts can be performed from the same IP address.
2. In 1 second, 10 search requests can be performed from the same IP adress by unauthenticated users, while authenticated users can perform 30 search requests per second.
Supported rate limiters: Supported rate limiters:
* `:search` for the search requests (account & status search etc.) * `:search` - Account/Status search.
* `:app_account_creation` for registering user accounts from the same IP address * `:app_account_creation` - Account registration from the API.
* `:relations_actions` for actions on relations with all users (follow, unfollow) * `:relations_actions` - Following/Unfollowing in general.
* `:relation_id_action` for actions on relation with a specific user (follow, unfollow) * `:relation_id_action` - Following/Unfollowing for a specific user.
* `:statuses_actions` for create / delete / fav / unfav / reblog / unreblog actions on any statuses * `:statuses_actions` - Status actions such as: (un)repeating, (un)favouriting, creating, deleting.
* `:status_id_action` for fav / unfav or reblog / unreblog actions on the same status by the same user * `:status_id_action` - (un)Repeating/(un)Favouriting a particular status.
* `:authentication` - Authentication actions, i.e getting an OAuth token.
* `:password_reset` - Requesting password reset emails.
* `:account_confirmation_resend` - Requesting resending account confirmation emails.
* `:ap_routes` - Requesting statuses via ActivityPub.
### :web_cache_ttl ### :web_cache_ttl

View file

@ -33,6 +33,7 @@ def user_agent do
def start(_type, _args) do def start(_type, _args) do
Pleroma.HTML.compile_scrubbers() Pleroma.HTML.compile_scrubbers()
Pleroma.Config.DeprecationWarnings.warn() Pleroma.Config.DeprecationWarnings.warn()
Pleroma.Plugs.HTTPSecurityPlug.warn_if_disabled()
Pleroma.Repo.check_migrations_applied!() Pleroma.Repo.check_migrations_applied!()
setup_instrumenters() setup_instrumenters()
load_custom_modules() load_custom_modules()

View file

@ -6,6 +6,8 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
alias Pleroma.Config alias Pleroma.Config
import Plug.Conn import Plug.Conn
require Logger
def init(opts), do: opts def init(opts), do: opts
def call(conn, _options) do def call(conn, _options) do
@ -90,6 +92,51 @@ defp csp_string do
|> Enum.join("; ") |> Enum.join("; ")
end end
def warn_if_disabled do
unless Config.get([:http_security, :enabled]) do
Logger.warn("
.i;;;;i.
iYcviii;vXY:
.YXi .i1c.
.YC. . in7.
.vc. ...... ;1c.
i7, .. .;1;
i7, .. ... .Y1i
,7v .6MMM@; .YX,
.7;. ..IMMMMMM1 :t7.
.;Y. ;$MMMMMM9. :tc.
vY. .. .nMMM@MMU. ;1v.
i7i ... .#MM@M@C. .....:71i
it: .... $MMM@9;.,i;;;i,;tti
:t7. ..... 0MMMWv.,iii:::,,;St.
.nC. ..... IMMMQ..,::::::,.,czX.
.ct: ....... .ZMMMI..,:::::::,,:76Y.
c2: ......,i..Y$M@t..:::::::,,..inZY
vov ......:ii..c$MBc..,,,,,,,,,,..iI9i
i9Y ......iii:..7@MA,..,,,,,,,,,....;AA:
iIS. ......:ii::..;@MI....,............;Ez.
.I9. ......:i::::...8M1..................C0z.
.z9; ......:i::::,.. .i:...................zWX.
vbv ......,i::::,,. ................. :AQY
c6Y. .,...,::::,,..:t0@@QY. ................ :8bi
:6S. ..,,...,:::,,,..EMMMMMMI. ............... .;bZ,
:6o, .,,,,..:::,,,..i#MMMMMM#v................. YW2.
.n8i ..,,,,,,,::,,,,.. tMMMMM@C:.................. .1Wn
7Uc. .:::,,,,,::,,,,.. i1t;,..................... .UEi
7C...::::::::::::,,,,.. .................... vSi.
;1;...,,::::::,......... .................. Yz:
v97,......... .voC.
izAotX7777777777777777777777777777777777777777Y7n92:
.;CoIIIIIUAA666666699999ZZZZZZZZZZZZZZZZZZZZ6ov.
HTTP Security is disabled. Please re-enable it to prevent users from attacking
your instance and your users via malicious posts:
config :pleroma, :http_security, enabled: true
")
end
end
defp maybe_send_sts_header(conn, true) do defp maybe_send_sts_header(conn, true) do
max_age_sts = Config.get([:http_security, :sts_max_age]) max_age_sts = Config.get([:http_security, :sts_max_age])
max_age_ct = Config.get([:http_security, :ct_max_age]) max_age_ct = Config.get([:http_security, :ct_max_age])

View file

@ -67,6 +67,8 @@ defmodule Pleroma.Plugs.RateLimiter do
alias Pleroma.Plugs.RateLimiter.LimiterSupervisor alias Pleroma.Plugs.RateLimiter.LimiterSupervisor
alias Pleroma.User alias Pleroma.User
require Logger
def init(opts) do def init(opts) do
limiter_name = Keyword.get(opts, :name) limiter_name = Keyword.get(opts, :name)
@ -89,6 +91,14 @@ def init(opts) do
def call(conn, nil), do: conn def call(conn, nil), do: conn
def call(conn, settings) do def call(conn, settings) do
case disabled?() do
true ->
if Pleroma.Config.get(:env) == :prod,
do: Logger.warn("Rate limiter is disabled for localhost/socket")
conn
false ->
settings settings
|> incorporate_conn_info(conn) |> incorporate_conn_info(conn)
|> check_rate() |> check_rate()
@ -100,6 +110,19 @@ def call(conn, settings) do
render_throttled_error(conn) render_throttled_error(conn)
end end
end end
end
def disabled? do
localhost_or_socket =
Pleroma.Config.get([Pleroma.Web.Endpoint, :http, :ip])
|> Tuple.to_list()
|> Enum.join(".")
|> String.match?(~r/^local|^127.0.0.1/)
remote_ip_disabled = not Pleroma.Config.get([Pleroma.Plugs.RemoteIp, :enabled])
localhost_or_socket and remote_ip_disabled
end
def inspect_bucket(conn, name_root, settings) do def inspect_bucket(conn, name_root, settings) do
settings = settings =

View file

@ -10,10 +10,7 @@ defmodule Pleroma.Plugs.RemoteIp do
@behaviour Plug @behaviour Plug
@headers ~w[ @headers ~w[
forwarded
x-forwarded-for x-forwarded-for
x-client-ip
x-real-ip
] ]
# https://en.wikipedia.org/wiki/Localhost # https://en.wikipedia.org/wiki/Localhost

View file

@ -325,12 +325,14 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
def react_with_emoji(user, object, emoji, options \\ []) do def react_with_emoji(user, object, emoji, options \\ []) do
with local <- Keyword.get(options, :local, true), with local <- Keyword.get(options, :local, true),
activity_id <- Keyword.get(options, :activity_id, nil), activity_id <- Keyword.get(options, :activity_id, nil),
Pleroma.Emoji.is_unicode_emoji?(emoji), true <- Pleroma.Emoji.is_unicode_emoji?(emoji),
reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id), reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),
{:ok, activity} <- insert(reaction_data, local), {:ok, activity} <- insert(reaction_data, local),
{:ok, object} <- add_emoji_reaction_to_object(activity, object), {:ok, object} <- add_emoji_reaction_to_object(activity, object),
:ok <- maybe_federate(activity) do :ok <- maybe_federate(activity) do
{:ok, activity, object} {:ok, activity, object}
else
e -> {:error, e}
end end
end end
@ -345,6 +347,8 @@ def unreact_with_emoji(user, reaction_id, options \\ []) do
{:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object), {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),
:ok <- maybe_federate(activity) do :ok <- maybe_federate(activity) do
{:ok, activity, object} {:ok, activity, object}
else
e -> {:error, e}
end end
end end

View file

@ -256,7 +256,11 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
emoji_reactions = emoji_reactions =
with %{data: %{"reactions" => emoji_reactions}} <- object do with %{data: %{"reactions" => emoji_reactions}} <- object do
Enum.map(emoji_reactions, fn [emoji, users] -> Enum.map(emoji_reactions, fn [emoji, users] ->
%{emoji: emoji, count: length(users)} %{
emoji: emoji,
count: length(users),
reacted: !!(opts[:for] && opts[:for].ap_id in users)
}
end) end)
else else
_ -> [] _ -> []

View file

@ -573,11 +573,14 @@ def update_file(conn, %{"action" => action}) do
assumed to be emojis and stored in the new `pack.json` file. assumed to be emojis and stored in the new `pack.json` file.
""" """
def import_from_fs(conn, _params) do def import_from_fs(conn, _params) do
with {:ok, results} <- File.ls(emoji_dir_path()) do emoji_path = emoji_dir_path()
with {:ok, %{access: :read_write}} <- File.stat(emoji_path),
{:ok, results} <- File.ls(emoji_path) do
imported_pack_names = imported_pack_names =
results results
|> Enum.filter(fn file -> |> Enum.filter(fn file ->
dir_path = Path.join(emoji_dir_path(), file) dir_path = Path.join(emoji_path, file)
# Find the directories that do NOT have pack.json # Find the directories that do NOT have pack.json
File.dir?(dir_path) and not File.exists?(Path.join(dir_path, "pack.json")) File.dir?(dir_path) and not File.exists?(Path.join(dir_path, "pack.json"))
end) end)
@ -585,6 +588,11 @@ def import_from_fs(conn, _params) do
json(conn, imported_pack_names) json(conn, imported_pack_names)
else else
{:ok, %{access: _}} ->
conn
|> put_status(:internal_server_error)
|> json(%{error: "Error: emoji pack directory must be writable"})
{:error, _} -> {:error, _} ->
conn conn
|> put_status(:internal_server_error) |> put_status(:internal_server_error)

View file

@ -47,13 +47,16 @@ def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id})
Object.normalize(activity) do Object.normalize(activity) do
reactions = reactions =
emoji_reactions emoji_reactions
|> Enum.map(fn [emoji, users] -> |> Enum.map(fn [emoji, user_ap_ids] ->
users = Enum.map(users, &User.get_cached_by_ap_id/1) users =
Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1)
|> Enum.filter(& &1)
%{ %{
emoji: emoji, emoji: emoji,
count: length(users), count: length(users),
accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}) accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}),
reacted: !!(user && user.ap_id in user_ap_ids)
} }
end) end)

View file

@ -48,6 +48,6 @@ defp maybe_put_title(meta, html) when meta != %{} do
defp maybe_put_title(meta, _), do: meta defp maybe_put_title(meta, _), do: meta
defp get_page_title(html) do defp get_page_title(html) do
Floki.find(html, "title") |> List.first() |> Floki.text() Floki.find(html, "html head title") |> List.first() |> Floki.text()
end end
end end

View file

@ -138,7 +138,8 @@ defp should_send?(%User{} = user, %Activity{} = item) do
with parent <- Object.normalize(item) || item, with parent <- Object.normalize(item) || item,
true <- true <-
Enum.all?([blocked_ap_ids, muted_ap_ids, reblog_muted_ap_ids], &(item.actor not in &1)), Enum.all?([blocked_ap_ids, muted_ap_ids], &(item.actor not in &1)),
true <- item.data["type"] != "Announce" || item.actor not in reblog_muted_ap_ids,
true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(parent.data["actor"] not in &1)), true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(parent.data["actor"] not in &1)),
true <- MapSet.disjoint?(recipients, recipient_blocks), true <- MapSet.disjoint?(recipients, recipient_blocks),
%{host: item_host} <- URI.parse(item.actor), %{host: item_host} <- URI.parse(item.actor),

View file

@ -1 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/vendors~app.b2603a50868c68a1c192.css rel=stylesheet><link href=/static/css/app.ae04505b31bb0ee2765e.css rel=stylesheet><link href=/static/fontello.1579102213354.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.86bc6d5e06d2e17976c5.js></script><script type=text/javascript src=/static/js/app.a43640742dacfb13b6b0.js></script></body></html> <!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/vendors~app.b2603a50868c68a1c192.css rel=stylesheet><link href=/static/css/app.ae04505b31bb0ee2765e.css rel=stylesheet><link href=/static/fontello.1580232989700.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.9ab182239f3a2abee89f.js></script><script type=text/javascript src=/static/js/app.9cfed8f3d06c299128ea.js></script></body></html>

View file

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

View file

@ -0,0 +1,136 @@
@font-face {
font-family: "Icons";
src: url("./font/fontello.1580232989700.eot");
src: url("./font/fontello.1580232989700.eot") format("embedded-opentype"),
url("./font/fontello.1580232989700.woff2") format("woff2"),
url("./font/fontello.1580232989700.woff") format("woff"),
url("./font/fontello.1580232989700.ttf") format("truetype"),
url("./font/fontello.1580232989700.svg") format("svg");
font-weight: normal;
font-style: normal;
}
[class^="icon-"]::before,
[class*=" icon-"]::before {
font-family: "Icons";
font-style: normal;
font-weight: normal;
speak: none;
display: inline-block;
text-decoration: inherit;
width: 1em;
margin-right: .2em;
text-align: center;
font-variant: normal;
text-transform: none;
line-height: 1em;
margin-left: .2em;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-spin4::before { content: "\e834"; }
.icon-cancel::before { content: "\e800"; }
.icon-upload::before { content: "\e801"; }
.icon-spin3::before { content: "\e832"; }
.icon-reply::before { content: "\f112"; }
.icon-star::before { content: "\e802"; }
.icon-star-empty::before { content: "\e803"; }
.icon-retweet::before { content: "\e804"; }
.icon-eye-off::before { content: "\e805"; }
.icon-binoculars::before { content: "\f1e5"; }
.icon-cog::before { content: "\e807"; }
.icon-user-plus::before { content: "\f234"; }
.icon-menu::before { content: "\f0c9"; }
.icon-logout::before { content: "\e808"; }
.icon-down-open::before { content: "\e809"; }
.icon-attach::before { content: "\e80a"; }
.icon-link-ext::before { content: "\f08e"; }
.icon-link-ext-alt::before { content: "\f08f"; }
.icon-picture::before { content: "\e80b"; }
.icon-video::before { content: "\e80c"; }
.icon-right-open::before { content: "\e80d"; }
.icon-left-open::before { content: "\e80e"; }
.icon-up-open::before { content: "\e80f"; }
.icon-comment-empty::before { content: "\f0e5"; }
.icon-mail-alt::before { content: "\f0e0"; }
.icon-lock::before { content: "\e811"; }
.icon-lock-open-alt::before { content: "\f13e"; }
.icon-globe::before { content: "\e812"; }
.icon-brush::before { content: "\e813"; }
.icon-search::before { content: "\e806"; }
.icon-adjust::before { content: "\e816"; }
.icon-thumbs-up-alt::before { content: "\f164"; }
.icon-attention::before { content: "\e814"; }
.icon-plus-squared::before { content: "\f0fe"; }
.icon-plus::before { content: "\e815"; }
.icon-edit::before { content: "\e817"; }
.icon-play-circled::before { content: "\f144"; }
.icon-pencil::before { content: "\e818"; }
.icon-chart-bar::before { content: "\e81b"; }
.icon-smile::before { content: "\f118"; }
.icon-bell-alt::before { content: "\f0f3"; }
.icon-wrench::before { content: "\e81a"; }
.icon-pin::before { content: "\e819"; }
.icon-ellipsis::before { content: "\f141"; }
.icon-bell-ringing-o::before { content: "\e810"; }
.icon-zoom-in::before { content: "\e81c"; }
.icon-gauge::before { content: "\f0e4"; }
.icon-users::before { content: "\e81d"; }
.icon-info-circled::before { content: "\e81f"; }
.icon-home-2::before { content: "\e821"; }
.icon-chat::before { content: "\e81e"; }
.icon-login::before { content: "\e820"; }
.icon-arrow-curved::before { content: "\e822"; }

View file

@ -1,2 +1,2 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{567:function(t,e,i){var c=i(568);"string"==typeof c&&(c=[[t.i,c,""]]),c.locals&&(t.exports=c.locals);(0,i(3).default)("cc6cdea4",c,!0,{})},568:function(t,e,i){(t.exports=i(2)(!1)).push([t.i,".sticker-picker{width:100%}.sticker-picker .contents{min-height:250px}.sticker-picker .contents .sticker-picker-content{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0 4px}.sticker-picker .contents .sticker-picker-content .sticker{display:-ms-flexbox;display:flex;-ms-flex:1 1 auto;flex:1 1 auto;margin:4px;width:56px;height:56px}.sticker-picker .contents .sticker-picker-content .sticker img{height:100%}.sticker-picker .contents .sticker-picker-content .sticker img:hover{filter:drop-shadow(0 0 5px var(--link,#d8a070))}",""])},569:function(t,e,i){"use strict";i.r(e);var c=i(88),n={components:{TabSwitcher:i(49).a},data:function(){return{meta:{stickers:[]},path:""}},computed:{pack:function(){return this.$store.state.instance.stickers||[]}},methods:{clear:function(){this.meta={stickers:[]}},pick:function(t,e){var i=this,n=this.$store;fetch(t).then(function(t){t.blob().then(function(t){var a=new File([t],e,{mimetype:"image/png"}),s=new FormData;s.append("file",a),c.a.uploadMedia({store:n,formData:s}).then(function(t){i.$emit("uploaded",t),i.clear()},function(t){console.warn("Can't attach sticker"),console.warn(t),i.$emit("upload-failed","default")})})})}}},a=i(0);var s=function(t){i(567)},r=Object(a.a)(n,function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"sticker-picker"},[i("tab-switcher",{staticClass:"tab-switcher",attrs:{"render-only-focused":!0,"scrollable-tabs":""}},t._l(t.pack,function(e){return i("div",{key:e.path,staticClass:"sticker-picker-content",attrs:{"image-tooltip":e.meta.title,image:e.path+e.meta.tabIcon}},t._l(e.meta.stickers,function(c){return i("div",{key:c,staticClass:"sticker",on:{click:function(i){i.stopPropagation(),i.preventDefault(),t.pick(e.path+c,e.meta.title)}}},[i("img",{attrs:{src:e.path+c}})])}),0)}),0)],1)},[],!1,s,null,null);e.default=r.exports}}]); (window.webpackJsonp=window.webpackJsonp||[]).push([[2],{575:function(t,e,i){var c=i(576);"string"==typeof c&&(c=[[t.i,c,""]]),c.locals&&(t.exports=c.locals);(0,i(3).default)("cc6cdea4",c,!0,{})},576:function(t,e,i){(t.exports=i(2)(!1)).push([t.i,".sticker-picker{width:100%}.sticker-picker .contents{min-height:250px}.sticker-picker .contents .sticker-picker-content{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:0 4px}.sticker-picker .contents .sticker-picker-content .sticker{display:-ms-flexbox;display:flex;-ms-flex:1 1 auto;flex:1 1 auto;margin:4px;width:56px;height:56px}.sticker-picker .contents .sticker-picker-content .sticker img{height:100%}.sticker-picker .contents .sticker-picker-content .sticker img:hover{filter:drop-shadow(0 0 5px var(--link,#d8a070))}",""])},577:function(t,e,i){"use strict";i.r(e);var c=i(88),n={components:{TabSwitcher:i(51).a},data:function(){return{meta:{stickers:[]},path:""}},computed:{pack:function(){return this.$store.state.instance.stickers||[]}},methods:{clear:function(){this.meta={stickers:[]}},pick:function(t,e){var i=this,n=this.$store;fetch(t).then(function(t){t.blob().then(function(t){var a=new File([t],e,{mimetype:"image/png"}),s=new FormData;s.append("file",a),c.a.uploadMedia({store:n,formData:s}).then(function(t){i.$emit("uploaded",t),i.clear()},function(t){console.warn("Can't attach sticker"),console.warn(t),i.$emit("upload-failed","default")})})})}}},a=i(0);var s=function(t){i(575)},r=Object(a.a)(n,function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"sticker-picker"},[i("tab-switcher",{staticClass:"tab-switcher",attrs:{"render-only-focused":!0,"scrollable-tabs":""}},t._l(t.pack,function(e){return i("div",{key:e.path,staticClass:"sticker-picker-content",attrs:{"image-tooltip":e.meta.title,image:e.path+e.meta.tabIcon}},t._l(e.meta.stickers,function(c){return i("div",{key:c,staticClass:"sticker",on:{click:function(i){i.stopPropagation(),i.preventDefault(),t.pick(e.path+c,e.meta.title)}}},[i("img",{attrs:{src:e.path+c}})])}),0)}),0)],1)},[],!1,s,null,null);e.default=r.exports}}]);
//# sourceMappingURL=2.8896ea39a0ea8016391a.js.map //# sourceMappingURL=2.59b096781ddca107175d.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
var serviceWorkerOption = {"assets":["/static/fontello.1579102213354.css","/static/font/fontello.1579102213354.eot","/static/font/fontello.1579102213354.svg","/static/font/fontello.1579102213354.ttf","/static/font/fontello.1579102213354.woff","/static/font/fontello.1579102213354.woff2","/static/img/nsfw.74818f9.png","/static/css/app.ae04505b31bb0ee2765e.css","/static/js/app.a43640742dacfb13b6b0.js","/static/css/vendors~app.b2603a50868c68a1c192.css","/static/js/vendors~app.86bc6d5e06d2e17976c5.js","/static/js/2.8896ea39a0ea8016391a.js"]}; var serviceWorkerOption = {"assets":["/static/fontello.1580232989700.css","/static/font/fontello.1580232989700.eot","/static/font/fontello.1580232989700.svg","/static/font/fontello.1580232989700.ttf","/static/font/fontello.1580232989700.woff","/static/font/fontello.1580232989700.woff2","/static/img/nsfw.74818f9.png","/static/css/app.ae04505b31bb0ee2765e.css","/static/js/app.9cfed8f3d06c299128ea.js","/static/css/vendors~app.b2603a50868c68a1c192.css","/static/js/vendors~app.9ab182239f3a2abee89f.js","/static/js/2.59b096781ddca107175d.js"]};
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/",t(t.s=1)}([function(e,n){ !function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/",t(t.s=1)}([function(e,n){
/*! /*!

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,30 @@
{
"type": "EmojiReaction",
"signature": {
"type": "RsaSignature2017",
"signatureValue": "fdxMfQSMwbC6wP6sh6neS/vM5879K67yQkHTbiT5Npr5wAac0y6+o3Ij+41tN3rL6wfuGTosSBTHOtta6R4GCOOhCaCSLMZKypnp1VltCzLDoyrZELnYQIC8gpUXVmIycZbREk22qWUe/w7DAFaKK4UscBlHDzeDVcA0K3Se5Sluqi9/Zh+ldAnEzj/rSEPDjrtvf5wGNf3fHxbKSRKFt90JvKK6hS+vxKUhlRFDf6/SMETw+EhwJSNW4d10yMUakqUWsFv4Acq5LW7l+HpYMvlYY1FZhNde1+uonnCyuQDyvzkff8zwtEJmAXC4RivO/VVLa17SmqheJZfI8oluVg==",
"creator": "http://mastodon.example.org/users/admin#main-key",
"created": "2018-02-17T18:57:49Z"
},
"object": "http://localtesting.pleroma.lol/objects/eb92579d-3417-42a8-8652-2492c2d4f454",
"content": "~",
"nickname": "lain",
"id": "http://mastodon.example.org/users/admin#reactions/2",
"actor": "http://mastodon.example.org/users/admin",
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"toot": "http://joinmastodon.org/ns#",
"sensitive": "as:sensitive",
"ostatus": "http://ostatus.org#",
"movedTo": "as:movedTo",
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"conversation": "ostatus:conversation",
"atomUri": "ostatus:atomUri",
"Hashtag": "as:Hashtag",
"Emoji": "toot:Emoji"
}
]
}

View file

@ -0,0 +1,30 @@
{
"type": "EmojiReaction",
"signature": {
"type": "RsaSignature2017",
"signatureValue": "fdxMfQSMwbC6wP6sh6neS/vM5879K67yQkHTbiT5Npr5wAac0y6+o3Ij+41tN3rL6wfuGTosSBTHOtta6R4GCOOhCaCSLMZKypnp1VltCzLDoyrZELnYQIC8gpUXVmIycZbREk22qWUe/w7DAFaKK4UscBlHDzeDVcA0K3Se5Sluqi9/Zh+ldAnEzj/rSEPDjrtvf5wGNf3fHxbKSRKFt90JvKK6hS+vxKUhlRFDf6/SMETw+EhwJSNW4d10yMUakqUWsFv4Acq5LW7l+HpYMvlYY1FZhNde1+uonnCyuQDyvzkff8zwtEJmAXC4RivO/VVLa17SmqheJZfI8oluVg==",
"creator": "http://mastodon.example.org/users/admin#main-key",
"created": "2018-02-17T18:57:49Z"
},
"object": "http://localtesting.pleroma.lol/objects/eb92579d-3417-42a8-8652-2492c2d4f454",
"content": "👌👌",
"nickname": "lain",
"id": "http://mastodon.example.org/users/admin#reactions/2",
"actor": "http://mastodon.example.org/users/admin",
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"toot": "http://joinmastodon.org/ns#",
"sensitive": "as:sensitive",
"ostatus": "http://ostatus.org#",
"movedTo": "as:movedTo",
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"conversation": "ostatus:conversation",
"atomUri": "ostatus:atomUri",
"Hashtag": "as:Hashtag",
"Emoji": "toot:Emoji"
}
]
}

File diff suppressed because one or more lines are too long

View file

@ -16,6 +16,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
test "config is required for plug to work" do test "config is required for plug to work" do
limiter_name = :test_init limiter_name = :test_init
Pleroma.Config.put([:rate_limit, limiter_name], {1, 1}) Pleroma.Config.put([:rate_limit, limiter_name], {1, 1})
Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
assert %{limits: {1, 1}, name: :test_init, opts: [name: :test_init]} == assert %{limits: {1, 1}, name: :test_init, opts: [name: :test_init]} ==
RateLimiter.init(name: limiter_name) RateLimiter.init(name: limiter_name)
@ -23,11 +24,39 @@ test "config is required for plug to work" do
assert nil == RateLimiter.init(name: :foo) assert nil == RateLimiter.init(name: :foo)
end end
test "it is disabled for localhost" do
limiter_name = :test_init
Pleroma.Config.put([:rate_limit, limiter_name], {1, 1})
Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {127, 0, 0, 1})
Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], false)
assert RateLimiter.disabled?() == true
end
test "it is disabled for socket" do
limiter_name = :test_init
Pleroma.Config.put([:rate_limit, limiter_name], {1, 1})
Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {:local, "/path/to/pleroma.sock"})
Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], false)
assert RateLimiter.disabled?() == true
end
test "it is enabled for socket when remote ip is enabled" do
limiter_name = :test_init
Pleroma.Config.put([:rate_limit, limiter_name], {1, 1})
Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {:local, "/path/to/pleroma.sock"})
Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], true)
assert RateLimiter.disabled?() == false
end
test "it restricts based on config values" do test "it restricts based on config values" do
limiter_name = :test_opts limiter_name = :test_opts
scale = 80 scale = 80
limit = 5 limit = 5
Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
Pleroma.Config.put([:rate_limit, limiter_name], {scale, limit}) Pleroma.Config.put([:rate_limit, limiter_name], {scale, limit})
opts = RateLimiter.init(name: limiter_name) opts = RateLimiter.init(name: limiter_name)
@ -61,6 +90,7 @@ test "`bucket_name` option overrides default bucket name" do
limiter_name = :test_bucket_name limiter_name = :test_bucket_name
Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5}) Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5})
Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
base_bucket_name = "#{limiter_name}:group1" base_bucket_name = "#{limiter_name}:group1"
opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name) opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name)
@ -75,6 +105,7 @@ test "`bucket_name` option overrides default bucket name" do
test "`params` option allows different queries to be tracked independently" do test "`params` option allows different queries to be tracked independently" do
limiter_name = :test_params limiter_name = :test_params
Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5}) Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5})
Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
opts = RateLimiter.init(name: limiter_name, params: ["id"]) opts = RateLimiter.init(name: limiter_name, params: ["id"])
@ -90,6 +121,7 @@ test "`params` option allows different queries to be tracked independently" do
test "it supports combination of options modifying bucket name" do test "it supports combination of options modifying bucket name" do
limiter_name = :test_options_combo limiter_name = :test_options_combo
Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5}) Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5})
Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
base_bucket_name = "#{limiter_name}:group1" base_bucket_name = "#{limiter_name}:group1"
opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name, params: ["id"]) opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name, params: ["id"])
@ -109,6 +141,7 @@ test "it supports combination of options modifying bucket name" do
test "are restricted based on remote IP" do test "are restricted based on remote IP" do
limiter_name = :test_unauthenticated limiter_name = :test_unauthenticated
Pleroma.Config.put([:rate_limit, limiter_name], [{1000, 5}, {1, 10}]) Pleroma.Config.put([:rate_limit, limiter_name], [{1000, 5}, {1, 10}])
Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
opts = RateLimiter.init(name: limiter_name) opts = RateLimiter.init(name: limiter_name)
@ -147,6 +180,7 @@ test "can have limits seperate from unauthenticated connections" do
scale = 50 scale = 50
limit = 5 limit = 5
Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
Pleroma.Config.put([:rate_limit, limiter_name], [{1000, 1}, {scale, limit}]) Pleroma.Config.put([:rate_limit, limiter_name], [{1000, 1}, {scale, limit}])
opts = RateLimiter.init(name: limiter_name) opts = RateLimiter.init(name: limiter_name)
@ -169,6 +203,7 @@ test "can have limits seperate from unauthenticated connections" do
test "diffrerent users are counted independently" do test "diffrerent users are counted independently" do
limiter_name = :test_authenticated limiter_name = :test_authenticated
Pleroma.Config.put([:rate_limit, limiter_name], [{1, 10}, {1000, 5}]) Pleroma.Config.put([:rate_limit, limiter_name], [{1, 10}, {1000, 5}])
Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
opts = RateLimiter.init(name: limiter_name) opts = RateLimiter.init(name: limiter_name)

View file

@ -395,6 +395,25 @@ test "it works for incoming emoji reactions" do
assert data["content"] == "👌" assert data["content"] == "👌"
end end
test "it reject invalid emoji reactions" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
data =
File.read!("test/fixtures/emoji-reaction-too-long.json")
|> Poison.decode!()
|> Map.put("object", activity.data["object"])
assert :error = Transmogrifier.handle_incoming(data)
data =
File.read!("test/fixtures/emoji-reaction-no-emoji.json")
|> Poison.decode!()
|> Map.put("object", activity.data["object"])
assert :error = Transmogrifier.handle_incoming(data)
end
test "it works for incoming emoji reaction undos" do test "it works for incoming emoji reaction undos" do
user = insert(:user) user = insert(:user)

View file

@ -238,7 +238,9 @@ test "reacting to a status with an emoji" do
assert reaction.data["actor"] == user.ap_id assert reaction.data["actor"] == user.ap_id
assert reaction.data["content"] == "👍" assert reaction.data["content"] == "👍"
# TODO: test error case. {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
{:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
end end
test "unreacting to a status with an emoji" do test "unreacting to a status with an emoji" do

View file

@ -668,6 +668,7 @@ test "returns error when user already registred", %{conn: conn, valid_params: va
end end
test "rate limit", %{conn: conn} do test "rate limit", %{conn: conn} do
Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], true)
app_token = insert(:oauth_token, user: nil) app_token = insert(:oauth_token, user: nil)
conn = conn =

View file

@ -37,8 +37,15 @@ test "has an emoji reaction list" do
status = StatusView.render("show.json", activity: activity) status = StatusView.render("show.json", activity: activity)
assert status[:pleroma][:emoji_reactions] == [ assert status[:pleroma][:emoji_reactions] == [
%{emoji: "", count: 2}, %{emoji: "", count: 2, reacted: false},
%{emoji: "🍵", count: 1} %{emoji: "🍵", count: 1, reacted: false}
]
status = StatusView.render("show.json", activity: activity, for: user)
assert status[:pleroma][:emoji_reactions] == [
%{emoji: "", count: 2, reacted: true},
%{emoji: "🍵", count: 1, reacted: false}
] ]
end end

View file

@ -6,7 +6,6 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
import Tesla.Mock import Tesla.Mock
import Pleroma.Factory import Pleroma.Factory
@emoji_dir_path Path.join( @emoji_dir_path Path.join(

View file

@ -25,9 +25,14 @@ test "POST /api/v1/pleroma/statuses/:id/react_with_emoji", %{conn: conn} do
|> assign(:user, other_user) |> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
|> post("/api/v1/pleroma/statuses/#{activity.id}/react_with_emoji", %{"emoji" => ""}) |> post("/api/v1/pleroma/statuses/#{activity.id}/react_with_emoji", %{"emoji" => ""})
|> json_response(200)
assert %{"id" => id} = json_response(result, 200) assert %{"id" => id} = result
assert to_string(activity.id) == id assert to_string(activity.id) == id
assert result["pleroma"]["emoji_reactions"] == [
%{"emoji" => "", "count" => 1, "reacted" => true}
]
end end
test "POST /api/v1/pleroma/statuses/:id/unreact_with_emoji", %{conn: conn} do test "POST /api/v1/pleroma/statuses/:id/unreact_with_emoji", %{conn: conn} do
@ -54,6 +59,7 @@ test "POST /api/v1/pleroma/statuses/:id/unreact_with_emoji", %{conn: conn} do
test "GET /api/v1/pleroma/statuses/:id/emoji_reactions_by", %{conn: conn} do test "GET /api/v1/pleroma/statuses/:id/emoji_reactions_by", %{conn: conn} do
user = insert(:user) user = insert(:user)
other_user = insert(:user) other_user = insert(:user)
doomed_user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"}) {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
@ -65,14 +71,29 @@ test "GET /api/v1/pleroma/statuses/:id/emoji_reactions_by", %{conn: conn} do
assert result == [] assert result == []
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, doomed_user, "🎅")
User.perform(:delete, doomed_user)
result = result =
conn conn
|> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by") |> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by")
|> json_response(200) |> json_response(200)
[%{"emoji" => "🎅", "count" => 1, "accounts" => [represented_user]}] = result [%{"emoji" => "🎅", "count" => 1, "accounts" => [represented_user], "reacted" => false}] =
result
assert represented_user["id"] == other_user.id assert represented_user["id"] == other_user.id
result =
conn
|> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:statuses"]))
|> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by")
|> json_response(200)
assert [%{"emoji" => "🎅", "count" => 1, "accounts" => [_represented_user], "reacted" => true}] =
result
end end
test "/api/v1/pleroma/conversations/:id" do test "/api/v1/pleroma/conversations/:id" do

View file

@ -85,4 +85,19 @@ test "respect only first title tag on the page" do
image: image_path image: image_path
}} }}
end end
test "takes first founded title in html head if there is html markup error" do
html = File.read!("test/fixtures/nypd-facial-recognition-children-teenagers4.html")
assert TwitterCard.parse(html, %{}) ==
{:ok,
%{
site: nil,
title:
"She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database. - The New York Times",
"app:id:googleplay": "com.nytimes.android",
"app:name:googleplay": "NYTimes",
"app:url:googleplay": "nytimes://reader/id/100000006583622"
}}
end
end end

View file

@ -65,6 +65,9 @@ test "it doesn't send notify to the 'user:notification' stream when a user is bl
blocked = insert(:user) blocked = insert(:user)
{:ok, _user_relationship} = User.block(user, blocked) {:ok, _user_relationship} = User.block(user, blocked)
{:ok, activity} = CommonAPI.post(user, %{"status" => ":("})
{:ok, notif, _} = CommonAPI.favorite(activity.id, blocked)
task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end) task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
Streamer.add_socket( Streamer.add_socket(
@ -72,9 +75,6 @@ test "it doesn't send notify to the 'user:notification' stream when a user is bl
%{transport_pid: task.pid, assigns: %{user: user}} %{transport_pid: task.pid, assigns: %{user: user}}
) )
{:ok, activity} = CommonAPI.post(user, %{"status" => ":("})
{:ok, notif, _} = CommonAPI.favorite(activity.id, blocked)
Streamer.stream("user:notification", notif) Streamer.stream("user:notification", notif)
Task.await(task) Task.await(task)
end end
@ -83,6 +83,11 @@ test "it doesn't send notify to the 'user:notification' stream when a thread is
user: user user: user
} do } do
user2 = insert(:user) user2 = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
{:ok, activity} = CommonAPI.add_mute(user, activity)
{:ok, notif, _} = CommonAPI.favorite(activity.id, user2)
task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end) task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
Streamer.add_socket( Streamer.add_socket(
@ -90,9 +95,6 @@ test "it doesn't send notify to the 'user:notification' stream when a thread is
%{transport_pid: task.pid, assigns: %{user: user}} %{transport_pid: task.pid, assigns: %{user: user}}
) )
{:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
{:ok, activity} = CommonAPI.add_mute(user, activity)
{:ok, notif, _} = CommonAPI.favorite(activity.id, user2)
Streamer.stream("user:notification", notif) Streamer.stream("user:notification", notif)
Task.await(task) Task.await(task)
end end
@ -101,6 +103,11 @@ test "it doesn't send notify to the 'user:notification' stream' when a domain is
user: user user: user
} do } do
user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"}) user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"})
{:ok, user} = User.block_domain(user, "hecking-lewd-place.com")
{:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
{:ok, notif, _} = CommonAPI.favorite(activity.id, user2)
task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end) task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
Streamer.add_socket( Streamer.add_socket(
@ -108,10 +115,6 @@ test "it doesn't send notify to the 'user:notification' stream' when a domain is
%{transport_pid: task.pid, assigns: %{user: user}} %{transport_pid: task.pid, assigns: %{user: user}}
) )
{:ok, user} = User.block_domain(user, "hecking-lewd-place.com")
{:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
{:ok, notif, _} = CommonAPI.favorite(activity.id, user2)
Streamer.stream("user:notification", notif) Streamer.stream("user:notification", notif)
Task.await(task) Task.await(task)
end end
@ -267,6 +270,8 @@ test "it doesn't send messages involving blocked users" do
blocked_user = insert(:user) blocked_user = insert(:user)
{:ok, _user_relationship} = User.block(user, blocked_user) {:ok, _user_relationship} = User.block(user, blocked_user)
{:ok, activity} = CommonAPI.post(blocked_user, %{"status" => "Test"})
task = task =
Task.async(fn -> Task.async(fn ->
refute_receive {:text, _}, 1_000 refute_receive {:text, _}, 1_000
@ -277,8 +282,6 @@ test "it doesn't send messages involving blocked users" do
user: user user: user
} }
{:ok, activity} = CommonAPI.post(blocked_user, %{"status" => "Test"})
topics = %{ topics = %{
"public" => [fake_socket] "public" => [fake_socket]
} }
@ -335,6 +338,12 @@ test "it doesn't send unwanted DMs to list" do
{:ok, list} = List.create("Test", user_a) {:ok, list} = List.create("Test", user_a)
{:ok, list} = List.follow(list, user_b) {:ok, list} = List.follow(list, user_b)
{:ok, activity} =
CommonAPI.post(user_b, %{
"status" => "@#{user_c.nickname} Test",
"visibility" => "direct"
})
task = task =
Task.async(fn -> Task.async(fn ->
refute_receive {:text, _}, 1_000 refute_receive {:text, _}, 1_000
@ -345,12 +354,6 @@ test "it doesn't send unwanted DMs to list" do
user: user_a user: user_a
} }
{:ok, activity} =
CommonAPI.post(user_b, %{
"status" => "@#{user_c.nickname} Test",
"visibility" => "direct"
})
topics = %{ topics = %{
"list:#{list.id}" => [fake_socket] "list:#{list.id}" => [fake_socket]
} }
@ -367,6 +370,12 @@ test "it doesn't send unwanted private posts to list" do
{:ok, list} = List.create("Test", user_a) {:ok, list} = List.create("Test", user_a)
{:ok, list} = List.follow(list, user_b) {:ok, list} = List.follow(list, user_b)
{:ok, activity} =
CommonAPI.post(user_b, %{
"status" => "Test",
"visibility" => "private"
})
task = task =
Task.async(fn -> Task.async(fn ->
refute_receive {:text, _}, 1_000 refute_receive {:text, _}, 1_000
@ -377,12 +386,6 @@ test "it doesn't send unwanted private posts to list" do
user: user_a user: user_a
} }
{:ok, activity} =
CommonAPI.post(user_b, %{
"status" => "Test",
"visibility" => "private"
})
topics = %{ topics = %{
"list:#{list.id}" => [fake_socket] "list:#{list.id}" => [fake_socket]
} }
@ -401,6 +404,12 @@ test "it sends wanted private posts to list" do
{:ok, list} = List.create("Test", user_a) {:ok, list} = List.create("Test", user_a)
{:ok, list} = List.follow(list, user_b) {:ok, list} = List.follow(list, user_b)
{:ok, activity} =
CommonAPI.post(user_b, %{
"status" => "Test",
"visibility" => "private"
})
task = task =
Task.async(fn -> Task.async(fn ->
assert_receive {:text, _}, 1_000 assert_receive {:text, _}, 1_000
@ -411,12 +420,6 @@ test "it sends wanted private posts to list" do
user: user_a user: user_a
} }
{:ok, activity} =
CommonAPI.post(user_b, %{
"status" => "Test",
"visibility" => "private"
})
Streamer.add_socket( Streamer.add_socket(
"list:#{list.id}", "list:#{list.id}",
fake_socket fake_socket
@ -433,6 +436,9 @@ test "it doesn't send muted reblogs" do
user3 = insert(:user) user3 = insert(:user)
CommonAPI.hide_reblogs(user1, user2) CommonAPI.hide_reblogs(user1, user2)
{:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
{:ok, announce_activity, _} = CommonAPI.repeat(create_activity.id, user2)
task = task =
Task.async(fn -> Task.async(fn ->
refute_receive {:text, _}, 1_000 refute_receive {:text, _}, 1_000
@ -443,9 +449,6 @@ test "it doesn't send muted reblogs" do
user: user1 user: user1
} }
{:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
{:ok, announce_activity, _} = CommonAPI.repeat(create_activity.id, user2)
topics = %{ topics = %{
"public" => [fake_socket] "public" => [fake_socket]
} }
@ -455,6 +458,34 @@ test "it doesn't send muted reblogs" do
Task.await(task) Task.await(task)
end end
test "it does send non-reblog notification for reblog-muted actors" do
user1 = insert(:user)
user2 = insert(:user)
user3 = insert(:user)
CommonAPI.hide_reblogs(user1, user2)
{:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
{:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, user2)
task =
Task.async(fn ->
assert_receive {:text, _}, 1_000
end)
fake_socket = %StreamerSocket{
transport_pid: task.pid,
user: user1
}
topics = %{
"public" => [fake_socket]
}
Worker.push_to_socket(topics, "public", favorite_activity)
Task.await(task)
end
test "it doesn't send posts from muted threads" do test "it doesn't send posts from muted threads" do
user = insert(:user) user = insert(:user)
user2 = insert(:user) user2 = insert(:user)