aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/mix/tasks/pleroma/common.ex10
-rw-r--r--lib/mix/tasks/pleroma/relay.ex4
-rw-r--r--lib/mix/tasks/pleroma/user.ex6
-rw-r--r--lib/pleroma/emoji.ex2
-rw-r--r--lib/pleroma/gopher/server.ex2
-rw-r--r--lib/pleroma/user.ex39
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex12
-rw-r--r--lib/pleroma/web/controller_helper.ex9
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex5
-rw-r--r--lib/pleroma/web/media_proxy/controller.ex9
-rw-r--r--lib/pleroma/web/media_proxy/media_proxy.ex9
-rw-r--r--lib/pleroma/web/nodeinfo/nodeinfo_controller.ex39
-rw-r--r--lib/pleroma/web/router.ex2
-rw-r--r--lib/pleroma/web/twitter_api/views/user_view.ex7
-rw-r--r--priv/repo/migrations/20181206125616_add_tags_to_users.exs11
-rw-r--r--test/media_proxy_test.exs37
-rw-r--r--test/web/admin_api/admin_api_controller_test.exs72
-rw-r--r--test/web/mastodon_api/account_view_test.exs6
-rw-r--r--test/web/twitter_api/views/user_view_test.exs12
19 files changed, 256 insertions, 37 deletions
diff --git a/lib/mix/tasks/pleroma/common.ex b/lib/mix/tasks/pleroma/common.ex
index 2e246c4b5..06893af05 100644
--- a/lib/mix/tasks/pleroma/common.ex
+++ b/lib/mix/tasks/pleroma/common.ex
@@ -4,13 +4,13 @@ defmodule Mix.Tasks.Pleroma.Common do
Mix.Task.run("app.start")
end
- def get_option(options, opt, prompt, def \\ nil, defname \\ nil) do
+ def get_option(options, opt, prompt, defval \\ nil, defname \\ nil) do
Keyword.get(options, opt) ||
- case Mix.shell().prompt("#{prompt} [#{defname || def}]") do
+ case Mix.shell().prompt("#{prompt} [#{defname || defval}]") do
"\n" ->
- case def do
- nil -> get_option(options, opt, prompt, def)
- def -> def
+ case defval do
+ nil -> get_option(options, opt, prompt, defval)
+ defval -> defval
end
opt ->
diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex
index 4aea52732..f4b7ff6a0 100644
--- a/lib/mix/tasks/pleroma/relay.ex
+++ b/lib/mix/tasks/pleroma/relay.ex
@@ -9,7 +9,7 @@ defmodule Mix.Tasks.Pleroma.Relay do
## Follow a remote relay
- ``mix pleroma.relay unfollow <relay_url>``
+ ``mix pleroma.relay follow <relay_url>``
Example: ``mix pleroma.relay follow https://example.org/relay``
@@ -33,7 +33,7 @@ defmodule Mix.Tasks.Pleroma.Relay do
def run(["unfollow", target]) do
Common.start_pleroma()
- with {:ok, activity} <- Relay.follow(target) do
+ with {:ok, activity} <- Relay.unfollow(target) do
# put this task to sleep to allow the genserver to push out the messages
:timer.sleep(500)
else
diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex
index 12b5af774..590553443 100644
--- a/lib/mix/tasks/pleroma/user.ex
+++ b/lib/mix/tasks/pleroma/user.ex
@@ -21,7 +21,7 @@ defmodule Mix.Tasks.Pleroma.User do
## Generate an invite link.
- mix pleroma.user invite
+ mix pleroma.user invite
## Delete the user's account.
@@ -31,6 +31,10 @@ defmodule Mix.Tasks.Pleroma.User do
mix pleroma.user toggle_activated NICKNAME
+ ## Unsubscribe local users from user's account and deactivate it
+
+ mix pleroma.user unsubscribe NICKNAME
+
## Create a password reset link.
mix pleroma.user reset_password NICKNAME
diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex
index 0a5e1d5ce..523dea652 100644
--- a/lib/pleroma/emoji.ex
+++ b/lib/pleroma/emoji.ex
@@ -10,7 +10,7 @@ defmodule Pleroma.Emoji do
"""
use GenServer
@ets __MODULE__.Ets
- @ets_options [:set, :protected, :named_table, {:read_concurrency, true}]
+ @ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}]
@doc false
def start_link() do
diff --git a/lib/pleroma/gopher/server.ex b/lib/pleroma/gopher/server.ex
index 3b0569a99..4d582ef25 100644
--- a/lib/pleroma/gopher/server.ex
+++ b/lib/pleroma/gopher/server.ex
@@ -22,7 +22,7 @@ defmodule Pleroma.Gopher.Server do
:gopher,
100,
:ranch_tcp,
- [port: port],
+ [ip: ip, port: port],
__MODULE__.ProtocolHandler,
[]
)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 9da674982..a290db04a 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -2,6 +2,7 @@ defmodule Pleroma.User do
use Ecto.Schema
import Ecto.{Changeset, Query}
+ alias Ecto.Multi
alias Pleroma.{Repo, User, Object, Web, Activity, Notification}
alias Comeonin.Pbkdf2
alias Pleroma.Formatter
@@ -23,6 +24,7 @@ defmodule Pleroma.User do
field(:local, :boolean, default: true)
field(:follower_address, :string)
field(:search_distance, :float, virtual: true)
+ field(:tags, {:array, :string}, default: [])
field(:last_refreshed_at, :naive_datetime)
has_many(:notifications, Notification)
embeds_one(:info, Pleroma.User.Info)
@@ -815,4 +817,41 @@ defmodule Pleroma.User do
CommonUtils.format_input(bio, mentions, tags, "text/plain") |> Formatter.emojify(emoji)
end
+
+ def tag(user_identifiers, tags) when is_list(user_identifiers) do
+ Repo.transaction(fn ->
+ for user_identifier <- user_identifiers, do: tag(user_identifier, tags)
+ end)
+ end
+
+ def untag(user_identifiers, tags) when is_list(user_identifiers) do
+ Repo.transaction(fn ->
+ for user_identifier <- user_identifiers, do: untag(user_identifier, tags)
+ end)
+ end
+
+ def tag(nickname, tags) when is_binary(nickname), do: tag(User.get_by_nickname(nickname), tags)
+
+ def untag(nickname, tags) when is_binary(nickname),
+ do: untag(User.get_by_nickname(nickname), tags)
+
+ def tag(%User{} = user, tags),
+ do: update_tags(user, Enum.uniq(user.tags ++ normalize_tags(tags)))
+
+ def untag(%User{} = user, tags), do: update_tags(user, user.tags -- normalize_tags(tags))
+
+ defp update_tags(%User{} = user, new_tags) do
+ {:ok, updated_user} =
+ user
+ |> change(%{tags: new_tags})
+ |> Repo.update()
+
+ updated_user
+ end
+
+ defp normalize_tags(tags) do
+ [tags]
+ |> List.flatten()
+ |> Enum.map(&String.downcase(&1))
+ end
end
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index 2c67d9cda..0bd85e0b6 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -3,6 +3,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
alias Pleroma.{User, Repo}
alias Pleroma.Web.ActivityPub.Relay
+ import Pleroma.Web.ControllerHelper, only: [json_response: 3]
+
require Logger
action_fallback(:errors)
@@ -40,6 +42,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|> json(new_user.nickname)
end
+ def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
+ with {:ok, _} <- User.tag(nicknames, tags),
+ do: json_response(conn, :no_content, "")
+ end
+
+ def untag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
+ with {:ok, _} <- User.untag(nicknames, tags),
+ do: json_response(conn, :no_content, "")
+ end
+
def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
when permission_group in ["moderator", "admin"] do
user = User.get_by_nickname(nickname)
diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex
new file mode 100644
index 000000000..ddf958811
--- /dev/null
+++ b/lib/pleroma/web/controller_helper.ex
@@ -0,0 +1,9 @@
+defmodule Pleroma.Web.ControllerHelper do
+ use Pleroma.Web, :controller
+
+ def json_response(conn, status, json) do
+ conn
+ |> put_status(status)
+ |> json(json)
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index bcfa8836e..ebcf9230b 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -58,6 +58,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
note: "",
privacy: user_info.default_scope,
sensitive: false
+ },
+
+ # Pleroma extension
+ pleroma: %{
+ tags: user.tags
}
}
end
diff --git a/lib/pleroma/web/media_proxy/controller.ex b/lib/pleroma/web/media_proxy/controller.ex
index e1b87e026..f496fc936 100644
--- a/lib/pleroma/web/media_proxy/controller.ex
+++ b/lib/pleroma/web/media_proxy/controller.ex
@@ -2,7 +2,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
use Pleroma.Web, :controller
alias Pleroma.{Web.MediaProxy, ReverseProxy}
- @default_proxy_opts [max_body_length: 25 * 1_048_576]
+ @default_proxy_opts [max_body_length: 25 * 1_048_576, http: [follow_redirect: true]]
def remote(conn, params = %{"sig" => sig64, "url" => url64}) do
with config <- Pleroma.Config.get([:media_proxy], []),
@@ -24,7 +24,12 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
end
def filename_matches(has_filename, path, url) do
- filename = MediaProxy.filename(url)
+ filename =
+ url
+ |> MediaProxy.filename()
+ |> URI.decode()
+
+ path = URI.decode(path)
cond do
has_filename && filename && Path.basename(path) != filename -> {:wrong_filename, filename}
diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex
index 28aacb0b1..902ab1b77 100644
--- a/lib/pleroma/web/media_proxy/media_proxy.ex
+++ b/lib/pleroma/web/media_proxy/media_proxy.ex
@@ -14,7 +14,14 @@ defmodule Pleroma.Web.MediaProxy do
url
else
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
- base64 = Base.url_encode64(url, @base64_opts)
+
+ # The URL is url-decoded and encoded again to ensure it is correctly encoded and not twice.
+ base64 =
+ url
+ |> URI.decode()
+ |> URI.encode()
+ |> Base.url_encode64(@base64_opts)
+
sig = :crypto.hmac(:sha, secret, base64)
sig64 = sig |> Base.url_encode64(@base64_opts)
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
index 2ea75cf16..277dc6ba1 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
@@ -71,23 +71,28 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
%{}
end
- features = [
- "pleroma_api",
- "mastodon_api",
- "mastodon_api_streaming",
- if Keyword.get(media_proxy, :enabled) do
- "media_proxy"
- end,
- if Keyword.get(gopher, :enabled) do
- "gopher"
- end,
- if Keyword.get(chat, :enabled) do
- "chat"
- end,
- if Keyword.get(suggestions, :enabled) do
- "suggestions"
- end
- ]
+ features =
+ [
+ "pleroma_api",
+ "mastodon_api",
+ "mastodon_api_streaming",
+ if Keyword.get(media_proxy, :enabled) do
+ "media_proxy"
+ end,
+ if Keyword.get(gopher, :enabled) do
+ "gopher"
+ end,
+ if Keyword.get(chat, :enabled) do
+ "chat"
+ end,
+ if Keyword.get(suggestions, :enabled) do
+ "suggestions"
+ end,
+ if Keyword.get(instance, :allow_relay) do
+ "relay"
+ end
+ ]
+ |> Enum.filter(& &1)
response = %{
version: "2.0",
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 75d965c6d..4d24d4c1c 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -98,6 +98,8 @@ defmodule Pleroma.Web.Router do
pipe_through(:admin_api)
delete("/user", AdminAPIController, :user_delete)
post("/user", AdminAPIController, :user_create)
+ put("/users/tag", AdminAPIController, :tag_users)
+ delete("/users/tag", AdminAPIController, :untag_users)
get("/permission_group/:nickname", AdminAPIController, :right_get)
get("/permission_group/:nickname/:permission_group", AdminAPIController, :right_get)
diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex
index b78024ed7..b3459af9a 100644
--- a/lib/pleroma/web/twitter_api/views/user_view.ex
+++ b/lib/pleroma/web/twitter_api/views/user_view.ex
@@ -77,7 +77,12 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
"locked" => user.info.locked,
"default_scope" => user.info.default_scope,
"no_rich_text" => user.info.no_rich_text,
- "fields" => fields
+ "fields" => fields,
+
+ # Pleroma extension
+ "pleroma" => %{
+ "tags" => user.tags
+ }
}
if assigns[:token] do
diff --git a/priv/repo/migrations/20181206125616_add_tags_to_users.exs b/priv/repo/migrations/20181206125616_add_tags_to_users.exs
new file mode 100644
index 000000000..1502f63b6
--- /dev/null
+++ b/priv/repo/migrations/20181206125616_add_tags_to_users.exs
@@ -0,0 +1,11 @@
+defmodule Pleroma.Repo.Migrations.AddTagsToUsers do
+ use Ecto.Migration
+
+ def change do
+ alter table(:users) do
+ add :tags, {:array, :string}
+ end
+
+ create index(:users, [:tags], using: :gin)
+ end
+end
diff --git a/test/media_proxy_test.exs b/test/media_proxy_test.exs
index d71f9f13a..cb455ca79 100644
--- a/test/media_proxy_test.exs
+++ b/test/media_proxy_test.exs
@@ -1,6 +1,7 @@
defmodule Pleroma.MediaProxyTest do
use ExUnit.Case
import Pleroma.Web.MediaProxy
+ alias Pleroma.Web.MediaProxy.MediaProxyController
describe "when enabled" do
setup do
@@ -65,6 +66,14 @@ defmodule Pleroma.MediaProxyTest do
assert decode_result(encoded) == url
end
+ test "ensures urls are url-encoded" do
+ assert decode_result(url("https://pleroma.social/Hello world.jpg")) ==
+ "https://pleroma.social/Hello%20world.jpg"
+
+ assert decode_result(url("https://pleroma.social/Hello%20world.jpg")) ==
+ "https://pleroma.social/Hello%20world.jpg"
+ end
+
test "validates signature" do
secret_key_base = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
@@ -83,6 +92,34 @@ defmodule Pleroma.MediaProxyTest do
assert decode_url(sig, base64) == {:error, :invalid_signature}
end
+ test "filename_matches matches url encoded paths" do
+ assert MediaProxyController.filename_matches(
+ true,
+ "/Hello%20world.jpg",
+ "http://pleroma.social/Hello world.jpg"
+ ) == :ok
+
+ assert MediaProxyController.filename_matches(
+ true,
+ "/Hello%20world.jpg",
+ "http://pleroma.social/Hello%20world.jpg"
+ ) == :ok
+ end
+
+ test "filename_matches matches non-url encoded paths" do
+ assert MediaProxyController.filename_matches(
+ true,
+ "/Hello world.jpg",
+ "http://pleroma.social/Hello%20world.jpg"
+ ) == :ok
+
+ assert MediaProxyController.filename_matches(
+ true,
+ "/Hello world.jpg",
+ "http://pleroma.social/Hello world.jpg"
+ ) == :ok
+ end
+
test "uses the configured base_url" do
base_url = Pleroma.Config.get([:media_proxy, :base_url])
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index 9634ad7c5..ba3b77fb6 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -37,6 +37,78 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
end
end
+ describe "PUT /api/pleroma/admin/users/tag" do
+ setup do
+ admin = insert(:user, info: %{is_admin: true})
+ user1 = insert(:user, %{tags: ["x"]})
+ user2 = insert(:user, %{tags: ["y"]})
+ user3 = insert(:user, %{tags: ["unchanged"]})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> put_req_header("accept", "application/json")
+ |> put(
+ "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=#{
+ user2.nickname
+ }&tags[]=foo&tags[]=bar"
+ )
+
+ %{conn: conn, user1: user1, user2: user2, user3: user3}
+ end
+
+ test "it appends specified tags to users with specified nicknames", %{
+ conn: conn,
+ user1: user1,
+ user2: user2
+ } do
+ assert json_response(conn, :no_content)
+ assert Repo.get(User, user1.id).tags == ["x", "foo", "bar"]
+ assert Repo.get(User, user2.id).tags == ["y", "foo", "bar"]
+ end
+
+ test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
+ assert json_response(conn, :no_content)
+ assert Repo.get(User, user3.id).tags == ["unchanged"]
+ end
+ end
+
+ describe "DELETE /api/pleroma/admin/users/tag" do
+ setup do
+ admin = insert(:user, info: %{is_admin: true})
+ user1 = insert(:user, %{tags: ["x"]})
+ user2 = insert(:user, %{tags: ["y", "z"]})
+ user3 = insert(:user, %{tags: ["unchanged"]})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> put_req_header("accept", "application/json")
+ |> delete(
+ "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=#{
+ user2.nickname
+ }&tags[]=x&tags[]=z"
+ )
+
+ %{conn: conn, user1: user1, user2: user2, user3: user3}
+ end
+
+ test "it removes specified tags from users with specified nicknames", %{
+ conn: conn,
+ user1: user1,
+ user2: user2
+ } do
+ assert json_response(conn, :no_content)
+ assert Repo.get(User, user1.id).tags == []
+ assert Repo.get(User, user2.id).tags == ["y"]
+ end
+
+ test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
+ assert json_response(conn, :no_content)
+ assert Repo.get(User, user3.id).tags == ["unchanged"]
+ end
+ end
+
describe "/api/pleroma/admin/permission_group" do
test "GET is giving user_info" do
admin = insert(:user, info: %{is_admin: true})
diff --git a/test/web/mastodon_api/account_view_test.exs b/test/web/mastodon_api/account_view_test.exs
index a2d3a2547..3cb9b9c5b 100644
--- a/test/web/mastodon_api/account_view_test.exs
+++ b/test/web/mastodon_api/account_view_test.exs
@@ -54,7 +54,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
note: "",
privacy: "public",
sensitive: false
- }
+ },
+ pleroma: %{tags: []}
}
assert expected == AccountView.render("account.json", %{user: user})
@@ -91,7 +92,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
note: "",
privacy: "public",
sensitive: false
- }
+ },
+ pleroma: %{tags: []}
}
assert expected == AccountView.render("account.json", %{user: user})
diff --git a/test/web/twitter_api/views/user_view_test.exs b/test/web/twitter_api/views/user_view_test.exs
index e69ca24a9..9898c217d 100644
--- a/test/web/twitter_api/views/user_view_test.exs
+++ b/test/web/twitter_api/views/user_view_test.exs
@@ -96,7 +96,8 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
"locked" => false,
"default_scope" => "public",
"no_rich_text" => false,
- "fields" => []
+ "fields" => [],
+ "pleroma" => %{"tags" => []}
}
assert represented == UserView.render("show.json", %{user: user})
@@ -137,7 +138,8 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
"locked" => false,
"default_scope" => "public",
"no_rich_text" => false,
- "fields" => []
+ "fields" => [],
+ "pleroma" => %{"tags" => []}
}
assert represented == UserView.render("show.json", %{user: user, for: follower})
@@ -179,7 +181,8 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
"locked" => false,
"default_scope" => "public",
"no_rich_text" => false,
- "fields" => []
+ "fields" => [],
+ "pleroma" => %{"tags" => []}
}
assert represented == UserView.render("show.json", %{user: follower, for: user})
@@ -228,7 +231,8 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do
"locked" => false,
"default_scope" => "public",
"no_rich_text" => false,
- "fields" => []
+ "fields" => [],
+ "pleroma" => %{"tags" => []}
}
blocker = Repo.get(User, blocker.id)