From f6fab01ba7a08fe0e5147f82d9e3dd294922dc93 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Sun, 17 Mar 2019 17:06:28 +0100 Subject: Web.Router: Add routes for Conversation mastoAPI --- lib/pleroma/web/router.ex | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index befd382ba..6d000b7f5 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -243,6 +243,9 @@ defmodule Pleroma.Web.Router do get("/suggestions", MastodonAPIController, :suggestions) + get("/conversations", MastodonAPIController, :conversations) + get("/conversations/:id/read", MastodonAPIController, :get_conversation) + get("/endorsements", MastodonAPIController, :empty_array) get("/pleroma/flavour", MastodonAPIController, :get_flavour) -- cgit v1.2.3 From a2e03d4f3cfc7ccae98ae19db253aec8f1d3e9d0 Mon Sep 17 00:00:00 2001 From: Zachary Dunn Date: Mon, 18 Mar 2019 13:56:59 +0000 Subject: Initial attempt at updating return type --- lib/pleroma/user.ex | 33 ++++++++++++++-------- lib/pleroma/web/activity_pub/relay.ex | 4 +-- lib/pleroma/web/activity_pub/transmogrifier.ex | 24 ++++++++-------- .../web/mastodon_api/mastodon_api_controller.ex | 2 +- lib/pleroma/web/twitter_api/twitter_api.ex | 2 +- 5 files changed, 37 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 1ce9882f6..6e8103c1c 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -411,7 +411,7 @@ defmodule Pleroma.User do Enum.map( followed_identifiers, fn followed_identifier -> - with %User{} = followed <- get_or_fetch(followed_identifier), + with {:ok, %User{} = followed} <- get_or_fetch(followed_identifier), {:ok, follower} <- maybe_direct_follow(follower, followed), {:ok, _} <- ActivityPub.follow(follower, followed) do followed @@ -492,7 +492,14 @@ defmodule Pleroma.User do def get_cached_by_nickname(nickname) do key = "nickname:#{nickname}" - Cachex.fetch!(:user_cache, key, fn _ -> get_or_fetch_by_nickname(nickname) end) + Cachex.fetch!(:user_cache, key, fn -> + user_result = get_or_fetch_by_nickname(nickname) + + case user_result do + {:ok, user} -> {:commit, user} + {:error, error} -> {:ignore, error} + end + end) end def get_cached_by_nickname_or_id(nickname_or_id) do @@ -529,7 +536,7 @@ defmodule Pleroma.User do def get_or_fetch_by_nickname(nickname) do with %User{} = user <- get_by_nickname(nickname) do - user + {:ok, user} else _e -> with [_nick, _domain] <- String.split(nickname, "@"), @@ -538,9 +545,9 @@ defmodule Pleroma.User do {:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user]) end - user + {:ok, user} else - _e -> nil + _e -> {:error, "Error"} end end end @@ -939,7 +946,7 @@ defmodule Pleroma.User do Enum.map( blocked_identifiers, fn blocked_identifier -> - with %User{} = blocked <- get_or_fetch(blocked_identifier), + with {:ok, %User{} = blocked} <- get_or_fetch(blocked_identifier), {:ok, blocker} <- block(blocker, blocked), {:ok, _} <- ActivityPub.block(blocker, blocked) do blocked @@ -1157,17 +1164,19 @@ defmodule Pleroma.User do user = get_by_ap_id(ap_id) if !is_nil(user) and !User.needs_update?(user) do - user + {:ok, user} else user = fetch_by_ap_id(ap_id) - if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do - with %User{} = user do + with %User{} = user do + if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do {:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user]) end - end - user + {:ok, user} + else + _ -> {:error, "Could not fetch by AP id"} + end end end @@ -1209,7 +1218,7 @@ defmodule Pleroma.User do end def get_public_key_for_ap_id(ap_id) do - with %User{} = user <- get_or_fetch_by_ap_id(ap_id), + with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id), {:ok, public_key} <- public_key_from_info(user.info) do {:ok, public_key} else diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index 01fef71b9..2bab9b03c 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -15,7 +15,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do def follow(target_instance) do with %User{} = local_user <- get_actor(), - %User{} = target_user <- User.get_or_fetch_by_ap_id(target_instance), + {:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_instance), {:ok, activity} <- ActivityPub.follow(local_user, target_user) do Logger.info("relay: followed instance: #{target_instance}; id=#{activity.data["id"]}") {:ok, activity} @@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do def unfollow(target_instance) do with %User{} = local_user <- get_actor(), - %User{} = target_user <- User.get_or_fetch_by_ap_id(target_instance), + {:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_instance), {:ok, activity} <- ActivityPub.unfollow(local_user, target_user) do Logger.info("relay: unfollowed instance: #{target_instance}: id=#{activity.data["id"]}") {:ok, activity} diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 1247e4b61..a90b934e5 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -373,7 +373,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> fix_addressing with nil <- Activity.get_create_by_object_ap_id(object["id"]), - %User{} = user <- User.get_or_fetch_by_ap_id(data["actor"]) do + {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do object = fix_object(data["object"]) params = %{ @@ -402,7 +402,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do %{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data ) do with %User{local: true} = followed <- User.get_cached_by_ap_id(followed), - %User{} = follower <- User.get_or_fetch_by_ap_id(follower), + {:ok, %User{} = follower} <- User.get_or_fetch_by_ap_id(follower), {:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do if not User.locked?(followed) do ActivityPub.accept(%{ @@ -425,7 +425,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do %{"type" => "Accept", "object" => follow_object, "actor" => _actor, "id" => _id} = data ) do with actor <- get_actor(data), - %User{} = followed <- User.get_or_fetch_by_ap_id(actor), + {:ok, %User{} = followed} <- User.get_or_fetch_by_ap_id(actor), {:ok, follow_activity} <- get_follow_activity(follow_object, followed), {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), @@ -451,7 +451,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do %{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => _id} = data ) do with actor <- get_actor(data), - %User{} = followed <- User.get_or_fetch_by_ap_id(actor), + {:ok, %User{} = followed} <- User.get_or_fetch_by_ap_id(actor), {:ok, follow_activity} <- get_follow_activity(follow_object, followed), {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), @@ -475,7 +475,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do %{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data ) do with actor <- get_actor(data), - %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor), {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), {:ok, activity, _object} <- ActivityPub.like(actor, object, id, false) do {:ok, activity} @@ -488,7 +488,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do %{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data ) do with actor <- get_actor(data), - %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor), {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), public <- Visibility.is_public?(data), {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do @@ -543,7 +543,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do object_id = Utils.get_ap_id(object_id) with actor <- get_actor(data), - %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor), {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), :ok <- contain_origin(actor.ap_id, object.data), {:ok, activity} <- ActivityPub.delete(object, false) do @@ -562,7 +562,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do } = data ) do with actor <- get_actor(data), - %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor), {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), {:ok, activity, _} <- ActivityPub.unannounce(actor, object, id, false) do {:ok, activity} @@ -580,7 +580,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do } = _data ) do with %User{local: true} = followed <- User.get_cached_by_ap_id(followed), - %User{} = follower <- User.get_or_fetch_by_ap_id(follower), + {:ok, %User{} = follower} <- User.get_or_fetch_by_ap_id(follower), {:ok, activity} <- ActivityPub.unfollow(follower, followed, id, false) do User.unfollow(follower, followed) {:ok, activity} @@ -599,7 +599,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do ) do with true <- Pleroma.Config.get([:activitypub, :accept_blocks]), %User{local: true} = blocked <- User.get_cached_by_ap_id(blocked), - %User{} = blocker <- User.get_or_fetch_by_ap_id(blocker), + {:ok, %User{} = blocker} <- User.get_or_fetch_by_ap_id(blocker), {:ok, activity} <- ActivityPub.unblock(blocker, blocked, id, false) do User.unblock(blocker, blocked) {:ok, activity} @@ -613,7 +613,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do ) do with true <- Pleroma.Config.get([:activitypub, :accept_blocks]), %User{local: true} = blocked = User.get_cached_by_ap_id(blocked), - %User{} = blocker = User.get_or_fetch_by_ap_id(blocker), + {:ok, %User{} = blocker = User.get_or_fetch_by_ap_id(blocker), {:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do User.unfollow(blocker, blocked) User.block(blocker, blocked) @@ -632,7 +632,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do } = data ) do with actor <- get_actor(data), - %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor), {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), {:ok, activity, _, _} <- ActivityPub.unlike(actor, object, id, false) do {:ok, activity} diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index e578f707e..cbf2a7ce1 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1450,7 +1450,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do x, "id", case User.get_or_fetch(x["acct"]) do - %{id: id} -> id + {:ok, %User{} = %{id: id}} -> id _ -> 0 end ) diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index d57100491..feadf8670 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -312,7 +312,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do end def get_external_profile(for_user, uri) do - with %User{} = user <- User.get_or_fetch(uri) do + with {:ok, %User{} = user} <- User.get_or_fetch(uri) do {:ok, UserView.render("show.json", %{user: user, for: for_user})} else _e -> -- cgit v1.2.3 From 5ba14c664b46faf633692cafa4290c0e6521d03c Mon Sep 17 00:00:00 2001 From: Zachary Dunn Date: Mon, 18 Mar 2019 14:05:10 +0000 Subject: Fix missing end brace --- lib/pleroma/web/activity_pub/transmogrifier.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index a90b934e5..546134f46 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -613,7 +613,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do ) do with true <- Pleroma.Config.get([:activitypub, :accept_blocks]), %User{local: true} = blocked = User.get_cached_by_ap_id(blocked), - {:ok, %User{} = blocker = User.get_or_fetch_by_ap_id(blocker), + {:ok, %User{} = blocker} = User.get_or_fetch_by_ap_id(blocker), {:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do User.unfollow(blocker, blocked) User.block(blocker, blocked) -- cgit v1.2.3 From af4338da0c26d992a5189fe940aa08ba69222e3b Mon Sep 17 00:00:00 2001 From: Zachary Dunn Date: Mon, 18 Mar 2019 14:52:24 +0000 Subject: Use better error message --- lib/pleroma/user.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 6e8103c1c..dcc03ae95 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -547,7 +547,7 @@ defmodule Pleroma.User do {:ok, user} else - _e -> {:error, "Error"} + e -> {:error, e} end end end -- cgit v1.2.3 From e572786dad231a954c87ce54bb63e9be7fb396e9 Mon Sep 17 00:00:00 2001 From: Zachary Dunn Date: Mon, 18 Mar 2019 14:53:30 +0000 Subject: Run --- lib/pleroma/user.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index dcc03ae95..a29f836be 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -492,6 +492,7 @@ defmodule Pleroma.User do def get_cached_by_nickname(nickname) do key = "nickname:#{nickname}" + Cachex.fetch!(:user_cache, key, fn -> user_result = get_or_fetch_by_nickname(nickname) -- cgit v1.2.3 From 97b35e00b049c8f908484163b5ffdbcb55db7867 Mon Sep 17 00:00:00 2001 From: Zachary Dunn Date: Mon, 18 Mar 2019 14:59:52 +0000 Subject: Fix with expression always matching --- lib/pleroma/user.ex | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index a29f836be..5e8dfc669 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1167,9 +1167,7 @@ defmodule Pleroma.User do if !is_nil(user) and !User.needs_update?(user) do {:ok, user} else - user = fetch_by_ap_id(ap_id) - - with %User{} = user do + with %User{} = user <- fetch_by_ap_id(ap_id) do if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do {:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user]) end -- cgit v1.2.3 From c810fb81a489dc3faeb1f21092c89a3131188ac1 Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 31 Mar 2019 14:55:09 +0200 Subject: Basic SSH daemon. --- lib/pleroma/bbs/bbs.ex | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 lib/pleroma/bbs/bbs.ex (limited to 'lib') diff --git a/lib/pleroma/bbs/bbs.ex b/lib/pleroma/bbs/bbs.ex new file mode 100644 index 000000000..486ab9183 --- /dev/null +++ b/lib/pleroma/bbs/bbs.ex @@ -0,0 +1,34 @@ +defmodule Pleroma.BBS do + def start_daemon do + :ok = :ssh.start() + + options = [ + system_dir: 'ssh_keys', + auth_method_kb_interactive_data: fn (_, user, _) -> { + 'Welcome to Pleroma BBS', + 'Hello #{user}', + 'Password: ', + false } + end, + auth_methods: 'keyboard-interactive,password', + pwdfun: fn(user, password) -> true end, + shell: &start_prompt/1 + ] + :ssh.daemon(13121, options) + end + + def start_prompt(user) do + spawn(__MODULE__, :prompt, [user]) + end + + def prompt(user) do + IO.puts("Hey #{user}.\n") + IO.puts("Here's your timeline:\n") + + user = Pleroma.User.get_cached_by_nickname(to_string(user)) + Pleroma.Web.TwitterAPI.TwitterAPI.fetch_friend_statuses(user) + |> Enum.each(fn (status) -> + IO.puts("#{status["user"]["name"]} (#{status["user"]["screen_name"]}): #{status["text"]}") + end) + end +end -- cgit v1.2.3 From 17ab9fa45bf8a359746d30f4a91cb57a9c94f206 Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 31 Mar 2019 18:01:16 +0200 Subject: BBS: Some more functionality. --- lib/pleroma/bbs/authenticator.ex | 16 +++++ lib/pleroma/bbs/bbs.ex | 34 ---------- lib/pleroma/bbs/handler.ex | 130 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 34 deletions(-) create mode 100644 lib/pleroma/bbs/authenticator.ex delete mode 100644 lib/pleroma/bbs/bbs.ex create mode 100644 lib/pleroma/bbs/handler.ex (limited to 'lib') diff --git a/lib/pleroma/bbs/authenticator.ex b/lib/pleroma/bbs/authenticator.ex new file mode 100644 index 000000000..a2c153720 --- /dev/null +++ b/lib/pleroma/bbs/authenticator.ex @@ -0,0 +1,16 @@ +defmodule Pleroma.BBS.Authenticator do + use Sshd.PasswordAuthenticator + alias Comeonin.Pbkdf2 + alias Pleroma.User + + def authenticate(username, password) do + username = to_string(username) + password = to_string(password) + + with %User{} = user <- User.get_by_nickname(username) do + Pbkdf2.checkpw(password, user.password_hash) + else + _e -> false + end + end +end diff --git a/lib/pleroma/bbs/bbs.ex b/lib/pleroma/bbs/bbs.ex deleted file mode 100644 index 486ab9183..000000000 --- a/lib/pleroma/bbs/bbs.ex +++ /dev/null @@ -1,34 +0,0 @@ -defmodule Pleroma.BBS do - def start_daemon do - :ok = :ssh.start() - - options = [ - system_dir: 'ssh_keys', - auth_method_kb_interactive_data: fn (_, user, _) -> { - 'Welcome to Pleroma BBS', - 'Hello #{user}', - 'Password: ', - false } - end, - auth_methods: 'keyboard-interactive,password', - pwdfun: fn(user, password) -> true end, - shell: &start_prompt/1 - ] - :ssh.daemon(13121, options) - end - - def start_prompt(user) do - spawn(__MODULE__, :prompt, [user]) - end - - def prompt(user) do - IO.puts("Hey #{user}.\n") - IO.puts("Here's your timeline:\n") - - user = Pleroma.User.get_cached_by_nickname(to_string(user)) - Pleroma.Web.TwitterAPI.TwitterAPI.fetch_friend_statuses(user) - |> Enum.each(fn (status) -> - IO.puts("#{status["user"]["name"]} (#{status["user"]["screen_name"]}): #{status["text"]}") - end) - end -end diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex new file mode 100644 index 000000000..010929ed2 --- /dev/null +++ b/lib/pleroma/bbs/handler.ex @@ -0,0 +1,130 @@ +defmodule Pleroma.BBS.Handler do + @moduledoc """ + An example implementation of `Sshd.ShellHandler`, implementing a very simple + Read-Eval-Loop, that does nothing. + """ + use Sshd.ShellHandler + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.ActivityPub.ActivityPub + + def on_shell(username, _pubkey, _ip, _port) do + :ok = IO.puts "Welcome to #{Pleroma.Config.get([:instance, :name])}!" + user = Pleroma.User.get_by_nickname(to_string(username)) + Logger.debug("#{inspect user}") + loop(run_state([user: user])) + end + + def on_connect(username, ip, port, method) do + Logger.debug fn -> + """ + Incoming SSH shell #{inspect self()} requested for #{username} from #{inspect ip}:#{inspect port} using #{inspect method} + """ + end + end + + def on_disconnect(username, ip, port) do + Logger.debug fn -> + "Disconnecting SSH shell for #{username} from #{inspect ip}:#{inspect port}" + end + end + + defp loop(state) do + self_pid = self() + counter = state.counter + prefix = state.prefix + user = state.user + + input = spawn(fn -> io_get(self_pid, prefix, counter, user.nickname) end) + wait_input state, input + end + + def puts_activity(activity) do + status = Pleroma.Web.MastodonAPI.StatusView.render("status.json", %{activity: activity}) + IO.puts("#{status.id} by #{status.account.display_name} (#{status.account.acct}):") + IO.puts(HtmlSanitizeEx.strip_tags(status.content)) + IO.puts("") + end + + def handle_command(state, "help") do + IO.puts("Available commands:") + IO.puts("help - This help") + IO.puts("home - Show the home timeline") + IO.puts("p - Post the given text") + IO.puts("quit - Quit") + + state + end + + def handle_command(%{user: user} = state, "p " <> text) do + text = String.trim(text) + + with {:ok, _activity} <- CommonAPI.post(user, %{"status" => text}) do + IO.puts("Posted!") + else + _e -> IO.puts("Could not post...") + end + state + end + + def handle_command(state, "home") do + user = state.user + params = + %{} + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("user", user) + + activities = + [user.ap_id | user.following] + |> ActivityPub.fetch_activities(params) + |> ActivityPub.contain_timeline(user) + |> Enum.reverse() + + Enum.each(activities, fn (activity) -> + puts_activity(activity) + end) + + state + end + + def handle_command(_state, command) do + IO.puts("Unknown command '#{command}'") + end + + defp wait_input(state, input) do + receive do + {:input, ^input, "quit\n"} -> + IO.puts "Exiting..." + + {:input, ^input, code} when is_binary(code) -> + code = String.trim(code) + + handle_command(state, code) + + loop(%{state | counter: state.counter + 1}) + + {:error, :interrupted} -> + IO.puts "Caught Ctrl+C..." + loop(%{state | counter: state.counter + 1}) + + {:input, ^input, msg} -> + :ok = Logger.warn "received unknown message: #{inspect msg}" + loop(%{state | counter: state.counter + 1}) + end + end + + defp run_state(opts) do + %{prefix: "pleroma", counter: 1, user: opts[:user]} + end + + defp io_get(pid, prefix, counter, username) do + prompt = prompt(prefix, counter, username) + send pid, {:input, self(), IO.gets(:stdio, prompt)} + end + + defp prompt(prefix, counter, username) do + prompt = "#{username}@#{prefix}:#{counter}>" + prompt <> " " + end +end -- cgit v1.2.3 From 10fdc080a0048a4776abb4bd1b5aa22d8c65e2da Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 31 Mar 2019 20:35:10 +0200 Subject: BBS: Tests and formatting. --- lib/pleroma/bbs/handler.ex | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index 010929ed2..d999dcf76 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -8,34 +8,36 @@ defmodule Pleroma.BBS.Handler do alias Pleroma.Web.ActivityPub.ActivityPub def on_shell(username, _pubkey, _ip, _port) do - :ok = IO.puts "Welcome to #{Pleroma.Config.get([:instance, :name])}!" + :ok = IO.puts("Welcome to #{Pleroma.Config.get([:instance, :name])}!") user = Pleroma.User.get_by_nickname(to_string(username)) - Logger.debug("#{inspect user}") - loop(run_state([user: user])) + Logger.debug("#{inspect(user)}") + loop(run_state(user: user)) end def on_connect(username, ip, port, method) do - Logger.debug fn -> + Logger.debug(fn -> """ - Incoming SSH shell #{inspect self()} requested for #{username} from #{inspect ip}:#{inspect port} using #{inspect method} + Incoming SSH shell #{inspect(self())} requested for #{username} from #{inspect(ip)}:#{ + inspect(port) + } using #{inspect(method)} """ - end + end) end def on_disconnect(username, ip, port) do - Logger.debug fn -> - "Disconnecting SSH shell for #{username} from #{inspect ip}:#{inspect port}" - end + Logger.debug(fn -> + "Disconnecting SSH shell for #{username} from #{inspect(ip)}:#{inspect(port)}" + end) end defp loop(state) do self_pid = self() - counter = state.counter - prefix = state.prefix - user = state.user + counter = state.counter + prefix = state.prefix + user = state.user input = spawn(fn -> io_get(self_pid, prefix, counter, user.nickname) end) - wait_input state, input + wait_input(state, input) end def puts_activity(activity) do @@ -63,11 +65,13 @@ defmodule Pleroma.BBS.Handler do else _e -> IO.puts("Could not post...") end + state end def handle_command(state, "home") do user = state.user + params = %{} |> Map.put("type", ["Create", "Announce"]) @@ -81,7 +85,7 @@ defmodule Pleroma.BBS.Handler do |> ActivityPub.contain_timeline(user) |> Enum.reverse() - Enum.each(activities, fn (activity) -> + Enum.each(activities, fn activity -> puts_activity(activity) end) @@ -95,7 +99,7 @@ defmodule Pleroma.BBS.Handler do defp wait_input(state, input) do receive do {:input, ^input, "quit\n"} -> - IO.puts "Exiting..." + IO.puts("Exiting...") {:input, ^input, code} when is_binary(code) -> code = String.trim(code) @@ -105,11 +109,11 @@ defmodule Pleroma.BBS.Handler do loop(%{state | counter: state.counter + 1}) {:error, :interrupted} -> - IO.puts "Caught Ctrl+C..." + IO.puts("Caught Ctrl+C...") loop(%{state | counter: state.counter + 1}) {:input, ^input, msg} -> - :ok = Logger.warn "received unknown message: #{inspect msg}" + :ok = Logger.warn("received unknown message: #{inspect(msg)}") loop(%{state | counter: state.counter + 1}) end end @@ -120,7 +124,7 @@ defmodule Pleroma.BBS.Handler do defp io_get(pid, prefix, counter, username) do prompt = prompt(prefix, counter, username) - send pid, {:input, self(), IO.gets(:stdio, prompt)} + send(pid, {:input, self(), IO.gets(:stdio, prompt)}) end defp prompt(prefix, counter, username) do -- cgit v1.2.3 From e3bf6655ba412268e4f5fee645609c9738e453ef Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 31 Mar 2019 21:14:21 +0200 Subject: Add replying. --- lib/pleroma/bbs/handler.ex | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index d999dcf76..7749eb3af 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -6,6 +6,7 @@ defmodule Pleroma.BBS.Handler do use Sshd.ShellHandler alias Pleroma.Web.CommonAPI alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Activity def on_shell(username, _pubkey, _ip, _port) do :ok = IO.puts("Welcome to #{Pleroma.Config.get([:instance, :name])}!") @@ -42,7 +43,7 @@ defmodule Pleroma.BBS.Handler do def puts_activity(activity) do status = Pleroma.Web.MastodonAPI.StatusView.render("status.json", %{activity: activity}) - IO.puts("#{status.id} by #{status.account.display_name} (#{status.account.acct}):") + IO.puts("-- #{status.id} by #{status.account.display_name} (#{status.account.acct})") IO.puts(HtmlSanitizeEx.strip_tags(status.content)) IO.puts("") end @@ -52,11 +53,27 @@ defmodule Pleroma.BBS.Handler do IO.puts("help - This help") IO.puts("home - Show the home timeline") IO.puts("p - Post the given text") + IO.puts("r - Reply to the post with the given id") IO.puts("quit - Quit") state end + def handle_command(%{user: user} = state, "r " <> text) do + text = String.trim(text) + [activity_id, rest] = String.split(text, " ", parts: 2) + + with %Activity{} <- Activity.get_by_id(activity_id), + {:ok, _activity} <- + CommonAPI.post(user, %{"status" => rest, "in_reply_to_status_id" => activity_id}) do + IO.puts("Replied!") + else + _e -> IO.puts("Could not reply...") + end + + state + end + def handle_command(%{user: user} = state, "p " <> text) do text = String.trim(text) @@ -104,7 +121,7 @@ defmodule Pleroma.BBS.Handler do {:input, ^input, code} when is_binary(code) -> code = String.trim(code) - handle_command(state, code) + state = handle_command(state, code) loop(%{state | counter: state.counter + 1}) -- cgit v1.2.3 From 629ad1766ce5da434bf095f6baa81a460334e1b2 Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 31 Mar 2019 21:53:17 +0200 Subject: BBS: Some fixes. --- lib/pleroma/bbs/handler.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index 7749eb3af..1ebba77d2 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -91,7 +91,7 @@ defmodule Pleroma.BBS.Handler do params = %{} - |> Map.put("type", ["Create", "Announce"]) + |> Map.put("type", ["Create"]) |> Map.put("blocking_user", user) |> Map.put("muting_user", user) |> Map.put("user", user) @@ -100,7 +100,6 @@ defmodule Pleroma.BBS.Handler do [user.ap_id | user.following] |> ActivityPub.fetch_activities(params) |> ActivityPub.contain_timeline(user) - |> Enum.reverse() Enum.each(activities, fn activity -> puts_activity(activity) @@ -109,8 +108,9 @@ defmodule Pleroma.BBS.Handler do state end - def handle_command(_state, command) do + def handle_command(state, command) do IO.puts("Unknown command '#{command}'") + state end defp wait_input(state, input) do -- cgit v1.2.3 From d1da6b155ab758ae4eb8fa154997a0a2a179897c Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 09:34:53 +0200 Subject: Conversation: Add Conversations and Participations. --- lib/conversation.ex | 30 ++++++++++++++++++++++++++++++ lib/conversation/participation.ex | 31 +++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 lib/conversation.ex create mode 100644 lib/conversation/participation.ex (limited to 'lib') diff --git a/lib/conversation.ex b/lib/conversation.ex new file mode 100644 index 000000000..cfb78d925 --- /dev/null +++ b/lib/conversation.ex @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Conversation do + alias Pleroma.Repo + alias Pleroma.Conversation.Participation + use Ecto.Schema + import Ecto.Changeset + + schema "conversations" do + field(:ap_id, :string) + has_many(:participations, Participation) + + timestamps() + end + + def creation_cng(struct, params) do + struct + |> cast(params, [:ap_id]) + |> validate_required([:ap_id]) + |> unique_constraint(:ap_id) + end + + def create_for_ap_id(ap_id) do + %__MODULE__{} + |> creation_cng(%{ap_id: ap_id}) + |> Repo.insert() + end +end diff --git a/lib/conversation/participation.ex b/lib/conversation/participation.ex new file mode 100644 index 000000000..244d37c46 --- /dev/null +++ b/lib/conversation/participation.ex @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Conversation.Participation do + use Ecto.Schema + alias Pleroma.User + alias Pleroma.Conversation + alias Pleroma.Repo + import Ecto.Changeset + + schema "conversation_participations" do + belongs_to(:user, User, type: Pleroma.FlakeId) + belongs_to(:conversation, Conversation) + field(:read, :boolean, default: false) + + timestamps() + end + + def creation_cng(struct, params) do + struct + |> cast(params, [:user_id, :conversation_id]) + |> validate_required([:user_id, :conversation_id]) + end + + def create_for_user_and_conversation(user, conversation) do + %__MODULE__{} + |> creation_cng(%{user_id: user.id, conversation_id: conversation.id}) + |> Repo.insert() + end +end -- cgit v1.2.3 From 64c1c3a4071f3f99a59f38e2dcde499bda3969cf Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 15:12:01 +0200 Subject: Participations: Add marking as read and unread. --- lib/conversation/participation.ex | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'lib') diff --git a/lib/conversation/participation.ex b/lib/conversation/participation.ex index 244d37c46..ab59a529e 100644 --- a/lib/conversation/participation.ex +++ b/lib/conversation/participation.ex @@ -28,4 +28,22 @@ defmodule Pleroma.Conversation.Participation do |> creation_cng(%{user_id: user.id, conversation_id: conversation.id}) |> Repo.insert() end + + def read_cng(struct, params) do + struct + |> cast(params, [:read]) + |> validate_required([:read]) + end + + def mark_as_read(participation) do + participation + |> read_cng(%{read: true}) + |> Repo.update() + end + + def mark_as_unread(participation) do + participation + |> read_cng(%{read: false}) + |> Repo.update() + end end -- cgit v1.2.3 From 280172f6f6d74872349e3b4e6f1feaa9c95b3900 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 16:33:45 +0200 Subject: Conversations: Create or bump on inserting a dm. --- lib/conversation.ex | 42 +++++++++++++++++++++++++++- lib/conversation/participation.ex | 6 +++- lib/pleroma/web/activity_pub/activity_pub.ex | 2 ++ 3 files changed, 48 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/conversation.ex b/lib/conversation.ex index cfb78d925..3d53e91b7 100644 --- a/lib/conversation.ex +++ b/lib/conversation.ex @@ -5,10 +5,12 @@ defmodule Pleroma.Conversation do alias Pleroma.Repo alias Pleroma.Conversation.Participation + alias Pleroma.User use Ecto.Schema import Ecto.Changeset schema "conversations" do + # This is the context ap id. field(:ap_id, :string) has_many(:participations, Participation) @@ -25,6 +27,44 @@ defmodule Pleroma.Conversation do def create_for_ap_id(ap_id) do %__MODULE__{} |> creation_cng(%{ap_id: ap_id}) - |> Repo.insert() + |> Repo.insert( + on_conflict: [set: [updated_at: NaiveDateTime.utc_now()]], + returning: true, + conflict_target: :ap_id + ) + end + + def get_for_ap_id(ap_id) do + Repo.get_by(__MODULE__, ap_id: ap_id) + end + + @doc """ + This will + 1. Create a conversation if there isn't one already + 2. Create a participation for all the people involved who don't have one already + 3. Bump all relevant participations to 'unread' + """ + def create_or_bump_for(activity) do + with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), + "Create" <- activity.data["type"], + "Note" <- activity.data["object"]["type"], + ap_id when is_binary(ap_id) <- activity.data["object"]["context"] do + {:ok, conversation} = create_for_ap_id(ap_id) + + local_users = User.get_users_from_set(activity.recipients, true) + + participations = + Enum.map(local_users, fn user -> + {:ok, participation} = + Participation.create_for_user_and_conversation(user, conversation) + + participation + end) + + %{ + conversation + | participations: participations + } + end end end diff --git a/lib/conversation/participation.ex b/lib/conversation/participation.ex index ab59a529e..a58d0ca0d 100644 --- a/lib/conversation/participation.ex +++ b/lib/conversation/participation.ex @@ -26,7 +26,11 @@ defmodule Pleroma.Conversation.Participation do def create_for_user_and_conversation(user, conversation) do %__MODULE__{} |> creation_cng(%{user_id: user.id, conversation_id: conversation.id}) - |> Repo.insert() + |> Repo.insert( + on_conflict: [set: [read: false, updated_at: NaiveDateTime.utc_now()]], + returning: true, + conflict_target: [:user_id, :conversation_id] + ) end def read_cng(struct, params) do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index f217e7bac..880d19a5e 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.Activity + alias Pleroma.Conversation alias Pleroma.Instances alias Pleroma.Notification alias Pleroma.Object @@ -143,6 +144,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end) Notification.create_notifications(activity) + Conversation.create_or_bump_for(activity) stream_out(activity) {:ok, activity} else -- cgit v1.2.3 From 20d9b9076051d2dea60919ad85aaf88154629dc4 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 17:05:33 +0200 Subject: Participation: Get for a user. --- lib/conversation.ex | 4 ++-- lib/conversation/participation.ex | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/conversation.ex b/lib/conversation.ex index 3d53e91b7..6eedc72b6 100644 --- a/lib/conversation.ex +++ b/lib/conversation.ex @@ -51,10 +51,10 @@ defmodule Pleroma.Conversation do ap_id when is_binary(ap_id) <- activity.data["object"]["context"] do {:ok, conversation} = create_for_ap_id(ap_id) - local_users = User.get_users_from_set(activity.recipients, true) + users = User.get_users_from_set(activity.recipients) participations = - Enum.map(local_users, fn user -> + Enum.map(users, fn user -> {:ok, participation} = Participation.create_for_user_and_conversation(user, conversation) diff --git a/lib/conversation/participation.ex b/lib/conversation/participation.ex index a58d0ca0d..23e6409f1 100644 --- a/lib/conversation/participation.ex +++ b/lib/conversation/participation.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Conversation.Participation do alias Pleroma.Conversation alias Pleroma.Repo import Ecto.Changeset + import Ecto.Query schema "conversation_participations" do belongs_to(:user, User, type: Pleroma.FlakeId) @@ -50,4 +51,12 @@ defmodule Pleroma.Conversation.Participation do |> read_cng(%{read: false}) |> Repo.update() end + + def for_user(user, params \\ %{}) do + from(p in __MODULE__, + where: p.user_id == ^user.id, + order_by: [desc: p.updated_at] + ) + |> Pleroma.Pagination.fetch_paginated(params) + end end -- cgit v1.2.3 From cf353514feff50c2ccb9a8079ce5e695eb7f8cb6 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 17:28:02 +0200 Subject: Participations: Add last activity. --- lib/conversation/participation.ex | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'lib') diff --git a/lib/conversation/participation.ex b/lib/conversation/participation.ex index 23e6409f1..4183af8a7 100644 --- a/lib/conversation/participation.ex +++ b/lib/conversation/participation.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Conversation.Participation do alias Pleroma.User alias Pleroma.Conversation alias Pleroma.Repo + alias Pleroma.Web.ActivityPub.ActivityPub import Ecto.Changeset import Ecto.Query @@ -14,6 +15,7 @@ defmodule Pleroma.Conversation.Participation do belongs_to(:user, User, type: Pleroma.FlakeId) belongs_to(:conversation, Conversation) field(:read, :boolean, default: false) + field(:last_activity_id, Pleroma.FlakeId, virtual: true) timestamps() end @@ -59,4 +61,29 @@ defmodule Pleroma.Conversation.Participation do ) |> Pleroma.Pagination.fetch_paginated(params) end + + def for_user_with_last_activity_id(user, params \\ %{}) do + for_user(user, params) + |> Repo.preload(:conversation) + |> Enum.map(fn participation -> + # TODO: Don't load all those activities, just get the most recent + # Involves splitting up the query. + activities = + ActivityPub.fetch_activities_for_context(participation.conversation.ap_id, %{ + "user" => user, + "blocking_user" => user + }) + + activity_id = + case activities do + [activity | _] -> activity.id + _ -> nil + end + + %{ + participation + | last_activity_id: activity_id + } + end) + end end -- cgit v1.2.3 From c352a0aba601ae444bf5b479ab3c643728a8b35e Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 17:48:31 +0200 Subject: Conversations: Make tests run. --- .../web/mastodon_api/mastodon_api_controller.ex | 36 ++++++++++++++++++++++ lib/pleroma/web/router.ex | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 5462ce8be..57f73dacd 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Ecto.Changeset alias Pleroma.Activity alias Pleroma.Config + alias Pleroma.Conversation.Participation alias Pleroma.Filter alias Pleroma.Notification alias Pleroma.Object @@ -1584,6 +1585,41 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end + def conversations(%{assigns: %{user: user}} = conn, params) do + participations = Participation.for_user_with_last_activity_id(user, params) + + conversations = + Enum.map(participations, fn participation -> + %{ + id: participation.id, + # TODO: Add this. + accounts: [], + unread: !participation.read, + last_status: participation.last_activity_id + } + end) + + conn + |> add_link_headers(:conversations, participations) + |> json(conversations) + end + + def conversation_read(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do + with %Participation{} = participation <- + Repo.get_by(Participation, id: participation_id, user_id: user.id), + {:ok, participation} <- Participation.mark_as_read(participation) do + conn + |> json(%{ + id: participation.id, + # TODO: Add this. + accounts: [], + unread: !participation.read, + # TODO: Add this. + last_status: nil + }) + end + end + def try_render(conn, target, params) when is_binary(target) do res = render(conn, target, params) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 0af743b80..dc5119c50 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -273,7 +273,7 @@ defmodule Pleroma.Web.Router do get("/suggestions", MastodonAPIController, :suggestions) get("/conversations", MastodonAPIController, :conversations) - get("/conversations/:id/read", MastodonAPIController, :get_conversation) + post("/conversations/:id/read", MastodonAPIController, :conversation_read) get("/endorsements", MastodonAPIController, :empty_array) -- cgit v1.2.3 From d115d2a27e2e7a9df466fc4393416f804cb7e8e2 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 10 Apr 2019 18:17:22 +0200 Subject: Conversations: Tidying up. --- lib/conversation.ex | 70 ------------------------ lib/conversation/participation.ex | 89 ------------------------------- lib/pleroma/conversation.ex | 70 ++++++++++++++++++++++++ lib/pleroma/conversation/participation.ex | 89 +++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 159 deletions(-) delete mode 100644 lib/conversation.ex delete mode 100644 lib/conversation/participation.ex create mode 100644 lib/pleroma/conversation.ex create mode 100644 lib/pleroma/conversation/participation.ex (limited to 'lib') diff --git a/lib/conversation.ex b/lib/conversation.ex deleted file mode 100644 index 6eedc72b6..000000000 --- a/lib/conversation.ex +++ /dev/null @@ -1,70 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Conversation do - alias Pleroma.Repo - alias Pleroma.Conversation.Participation - alias Pleroma.User - use Ecto.Schema - import Ecto.Changeset - - schema "conversations" do - # This is the context ap id. - field(:ap_id, :string) - has_many(:participations, Participation) - - timestamps() - end - - def creation_cng(struct, params) do - struct - |> cast(params, [:ap_id]) - |> validate_required([:ap_id]) - |> unique_constraint(:ap_id) - end - - def create_for_ap_id(ap_id) do - %__MODULE__{} - |> creation_cng(%{ap_id: ap_id}) - |> Repo.insert( - on_conflict: [set: [updated_at: NaiveDateTime.utc_now()]], - returning: true, - conflict_target: :ap_id - ) - end - - def get_for_ap_id(ap_id) do - Repo.get_by(__MODULE__, ap_id: ap_id) - end - - @doc """ - This will - 1. Create a conversation if there isn't one already - 2. Create a participation for all the people involved who don't have one already - 3. Bump all relevant participations to 'unread' - """ - def create_or_bump_for(activity) do - with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), - "Create" <- activity.data["type"], - "Note" <- activity.data["object"]["type"], - ap_id when is_binary(ap_id) <- activity.data["object"]["context"] do - {:ok, conversation} = create_for_ap_id(ap_id) - - users = User.get_users_from_set(activity.recipients) - - participations = - Enum.map(users, fn user -> - {:ok, participation} = - Participation.create_for_user_and_conversation(user, conversation) - - participation - end) - - %{ - conversation - | participations: participations - } - end - end -end diff --git a/lib/conversation/participation.ex b/lib/conversation/participation.ex deleted file mode 100644 index 4183af8a7..000000000 --- a/lib/conversation/participation.ex +++ /dev/null @@ -1,89 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Conversation.Participation do - use Ecto.Schema - alias Pleroma.User - alias Pleroma.Conversation - alias Pleroma.Repo - alias Pleroma.Web.ActivityPub.ActivityPub - import Ecto.Changeset - import Ecto.Query - - schema "conversation_participations" do - belongs_to(:user, User, type: Pleroma.FlakeId) - belongs_to(:conversation, Conversation) - field(:read, :boolean, default: false) - field(:last_activity_id, Pleroma.FlakeId, virtual: true) - - timestamps() - end - - def creation_cng(struct, params) do - struct - |> cast(params, [:user_id, :conversation_id]) - |> validate_required([:user_id, :conversation_id]) - end - - def create_for_user_and_conversation(user, conversation) do - %__MODULE__{} - |> creation_cng(%{user_id: user.id, conversation_id: conversation.id}) - |> Repo.insert( - on_conflict: [set: [read: false, updated_at: NaiveDateTime.utc_now()]], - returning: true, - conflict_target: [:user_id, :conversation_id] - ) - end - - def read_cng(struct, params) do - struct - |> cast(params, [:read]) - |> validate_required([:read]) - end - - def mark_as_read(participation) do - participation - |> read_cng(%{read: true}) - |> Repo.update() - end - - def mark_as_unread(participation) do - participation - |> read_cng(%{read: false}) - |> Repo.update() - end - - def for_user(user, params \\ %{}) do - from(p in __MODULE__, - where: p.user_id == ^user.id, - order_by: [desc: p.updated_at] - ) - |> Pleroma.Pagination.fetch_paginated(params) - end - - def for_user_with_last_activity_id(user, params \\ %{}) do - for_user(user, params) - |> Repo.preload(:conversation) - |> Enum.map(fn participation -> - # TODO: Don't load all those activities, just get the most recent - # Involves splitting up the query. - activities = - ActivityPub.fetch_activities_for_context(participation.conversation.ap_id, %{ - "user" => user, - "blocking_user" => user - }) - - activity_id = - case activities do - [activity | _] -> activity.id - _ -> nil - end - - %{ - participation - | last_activity_id: activity_id - } - end) - end -end diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex new file mode 100644 index 000000000..a77a7cd6e --- /dev/null +++ b/lib/pleroma/conversation.ex @@ -0,0 +1,70 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Conversation do + alias Pleroma.Conversation.Participation + alias Pleroma.Repo + alias Pleroma.User + use Ecto.Schema + import Ecto.Changeset + + schema "conversations" do + # This is the context ap id. + field(:ap_id, :string) + has_many(:participations, Participation) + + timestamps() + end + + def creation_cng(struct, params) do + struct + |> cast(params, [:ap_id]) + |> validate_required([:ap_id]) + |> unique_constraint(:ap_id) + end + + def create_for_ap_id(ap_id) do + %__MODULE__{} + |> creation_cng(%{ap_id: ap_id}) + |> Repo.insert( + on_conflict: [set: [updated_at: NaiveDateTime.utc_now()]], + returning: true, + conflict_target: :ap_id + ) + end + + def get_for_ap_id(ap_id) do + Repo.get_by(__MODULE__, ap_id: ap_id) + end + + @doc """ + This will + 1. Create a conversation if there isn't one already + 2. Create a participation for all the people involved who don't have one already + 3. Bump all relevant participations to 'unread' + """ + def create_or_bump_for(activity) do + with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), + "Create" <- activity.data["type"], + "Note" <- activity.data["object"]["type"], + ap_id when is_binary(ap_id) <- activity.data["object"]["context"] do + {:ok, conversation} = create_for_ap_id(ap_id) + + users = User.get_users_from_set(activity.recipients) + + participations = + Enum.map(users, fn user -> + {:ok, participation} = + Participation.create_for_user_and_conversation(user, conversation) + + participation + end) + + %{ + conversation + | participations: participations + } + end + end +end diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex new file mode 100644 index 000000000..1a2ceafeb --- /dev/null +++ b/lib/pleroma/conversation/participation.ex @@ -0,0 +1,89 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Conversation.Participation do + use Ecto.Schema + alias Pleroma.Conversation + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + import Ecto.Changeset + import Ecto.Query + + schema "conversation_participations" do + belongs_to(:user, User, type: Pleroma.FlakeId) + belongs_to(:conversation, Conversation) + field(:read, :boolean, default: false) + field(:last_activity_id, Pleroma.FlakeId, virtual: true) + + timestamps() + end + + def creation_cng(struct, params) do + struct + |> cast(params, [:user_id, :conversation_id]) + |> validate_required([:user_id, :conversation_id]) + end + + def create_for_user_and_conversation(user, conversation) do + %__MODULE__{} + |> creation_cng(%{user_id: user.id, conversation_id: conversation.id}) + |> Repo.insert( + on_conflict: [set: [read: false, updated_at: NaiveDateTime.utc_now()]], + returning: true, + conflict_target: [:user_id, :conversation_id] + ) + end + + def read_cng(struct, params) do + struct + |> cast(params, [:read]) + |> validate_required([:read]) + end + + def mark_as_read(participation) do + participation + |> read_cng(%{read: true}) + |> Repo.update() + end + + def mark_as_unread(participation) do + participation + |> read_cng(%{read: false}) + |> Repo.update() + end + + def for_user(user, params \\ %{}) do + from(p in __MODULE__, + where: p.user_id == ^user.id, + order_by: [desc: p.updated_at] + ) + |> Pleroma.Pagination.fetch_paginated(params) + end + + def for_user_with_last_activity_id(user, params \\ %{}) do + for_user(user, params) + |> Repo.preload(:conversation) + |> Enum.map(fn participation -> + # TODO: Don't load all those activities, just get the most recent + # Involves splitting up the query. + activities = + ActivityPub.fetch_activities_for_context(participation.conversation.ap_id, %{ + "user" => user, + "blocking_user" => user + }) + + activity_id = + case activities do + [activity | _] -> activity.id + _ -> nil + end + + %{ + participation + | last_activity_id: activity_id + } + end) + end +end -- cgit v1.2.3 From c1ebb38d3adc1d222be832405ec0d7497b61f94a Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 15 Apr 2019 21:45:25 +0200 Subject: Conversation: Also create participations for remote users. Needed to get the participating user list. --- lib/pleroma/conversation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index a77a7cd6e..5a2a3fc6d 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -51,7 +51,7 @@ defmodule Pleroma.Conversation do ap_id when is_binary(ap_id) <- activity.data["object"]["context"] do {:ok, conversation} = create_for_ap_id(ap_id) - users = User.get_users_from_set(activity.recipients) + users = User.get_users_from_set(activity.recipients, false) participations = Enum.map(users, fn user -> -- cgit v1.2.3 From 0da985182f26927de0d2d9733816600afb89e79e Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 15 Apr 2019 21:58:58 +0200 Subject: Conversation: Return full status object, id is a string. --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 57f73dacd..3ffb767b9 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1590,12 +1590,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do conversations = Enum.map(participations, fn participation -> + activity = Activity.get_by_id_with_object(participation.last_activity_id) + + last_status = StatusView.render("status.json", %{activity: activity, for: user}) + %{ - id: participation.id, + id: participation.id |> to_string(), # TODO: Add this. accounts: [], unread: !participation.read, - last_status: participation.last_activity_id + last_status: last_status } end) -- cgit v1.2.3 From 76999c73a790232ff0c30fff7a51589fb44651a3 Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 15 Apr 2019 22:28:42 +0200 Subject: Conversation: Add accounts to output. --- lib/pleroma/conversation.ex | 1 + lib/pleroma/conversation/participation.ex | 1 + lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 9 +++++++-- 3 files changed, 9 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index 5a2a3fc6d..d9c84cb1b 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -13,6 +13,7 @@ defmodule Pleroma.Conversation do # This is the context ap id. field(:ap_id, :string) has_many(:participations, Participation) + has_many(:users, through: [:participations, :user]) timestamps() end diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index 1a2ceafeb..f200c1df5 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -60,6 +60,7 @@ defmodule Pleroma.Conversation.Participation do order_by: [desc: p.updated_at] ) |> Pleroma.Pagination.fetch_paginated(params) + |> Repo.preload(conversation: [:users]) end def for_user_with_last_activity_id(user, params \\ %{}) do diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 3ffb767b9..c7166ff28 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1594,10 +1594,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do last_status = StatusView.render("status.json", %{activity: activity, for: user}) + accounts = + AccountView.render("accounts.json", %{ + users: participation.conversation.users, + as: :user + }) + %{ id: participation.id |> to_string(), - # TODO: Add this. - accounts: [], + accounts: accounts, unread: !participation.read, last_status: last_status } -- cgit v1.2.3 From 24073f829f18d1ebd2cb23dd815c775b39145e42 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 21 Apr 2019 00:40:41 +0700 Subject: Refactor query to return only 1 message instead of 20 --- lib/pleroma/conversation/participation.ex | 13 ++----- lib/pleroma/web/activity_pub/activity_pub.ex | 53 ++++++++++++++++------------ 2 files changed, 33 insertions(+), 33 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex index f200c1df5..61021fb18 100644 --- a/lib/pleroma/conversation/participation.ex +++ b/lib/pleroma/conversation/participation.ex @@ -65,22 +65,13 @@ defmodule Pleroma.Conversation.Participation do def for_user_with_last_activity_id(user, params \\ %{}) do for_user(user, params) - |> Repo.preload(:conversation) |> Enum.map(fn participation -> - # TODO: Don't load all those activities, just get the most recent - # Involves splitting up the query. - activities = - ActivityPub.fetch_activities_for_context(participation.conversation.ap_id, %{ + activity_id = + ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{ "user" => user, "blocking_user" => user }) - activity_id = - case activities do - [activity | _] -> activity.id - _ -> nil - end - %{ participation | last_activity_id: activity_id diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 880d19a5e..577e6a59e 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -459,35 +459,44 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - def fetch_activities_for_context(context, opts \\ %{}) do + defp fetch_activities_for_context_query(context, opts) do public = ["https://www.w3.org/ns/activitystreams#Public"] recipients = if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public - query = from(activity in Activity) - - query = - query - |> restrict_blocked(opts) - |> restrict_recipients(recipients, opts["user"]) - - query = - from( - activity in query, - where: - fragment( - "?->>'type' = ? and ?->>'context' = ?", - activity.data, - "Create", - activity.data, - ^context - ), - order_by: [desc: :id] + from(activity in Activity) + |> restrict_blocked(opts) + |> restrict_recipients(recipients, opts["user"]) + |> where( + [activity], + fragment( + "?->>'type' = ? and ?->>'context' = ?", + activity.data, + "Create", + activity.data, + ^context ) - |> Activity.with_preloaded_object() + ) + |> order_by([activity], desc: activity.id) + end + + @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()] + def fetch_activities_for_context(context, opts \\ %{}) do + context + |> fetch_activities_for_context_query(opts) + |> Activity.with_preloaded_object() + |> Repo.all() + end - Repo.all(query) + @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) :: + Pleroma.FlakeId.t() | nil + def fetch_latest_activity_id_for_context(context, opts \\ %{}) do + context + |> fetch_activities_for_context_query(opts) + |> limit(1) + |> select([a], a.id) + |> Repo.one() end def fetch_public_activities(opts \\ %{}) do -- cgit v1.2.3 From 2662bea4e0d945edf7a24a44edf3ed39b2e64de9 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 21 Apr 2019 20:26:13 +0700 Subject: Add accounts and last_status to conversation read response --- .../web/mastodon_api/mastodon_api_controller.ex | 24 ++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index c7166ff28..86cacb0b0 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1617,14 +1617,30 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do with %Participation{} = participation <- Repo.get_by(Participation, id: participation_id, user_id: user.id), {:ok, participation} <- Participation.mark_as_read(participation) do + participation = Repo.preload(participation, conversation: :users) + + accounts = + AccountView.render("accounts.json", %{ + users: participation.conversation.users, + as: :user + }) + + last_activity_id = + ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{ + "user" => user, + "blocking_user" => user + }) + + activity = Activity.get_by_id_with_object(last_activity_id) + + last_status = StatusView.render("status.json", %{activity: activity, for: user}) + conn |> json(%{ id: participation.id, - # TODO: Add this. - accounts: [], + accounts: accounts, unread: !participation.read, - # TODO: Add this. - last_status: nil + last_status: last_status }) end end -- cgit v1.2.3 From e56afefef9c0f256561c6ecab79e5520fdeb6d5e Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 21 Apr 2019 23:14:27 +0700 Subject: Refactor conversation function in MastodonAPIController to use a View --- .../web/mastodon_api/mastodon_api_controller.ex | 44 +++------------------- .../web/mastodon_api/views/conversation_view.ex | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+), 39 deletions(-) create mode 100644 lib/pleroma/web/mastodon_api/views/conversation_view.ex (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 86cacb0b0..d5b6a943f 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -22,6 +22,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.AppView + alias Pleroma.Web.MastodonAPI.ConversationView alias Pleroma.Web.MastodonAPI.FilterView alias Pleroma.Web.MastodonAPI.ListView alias Pleroma.Web.MastodonAPI.MastodonAPI @@ -1590,22 +1591,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do conversations = Enum.map(participations, fn participation -> - activity = Activity.get_by_id_with_object(participation.last_activity_id) - - last_status = StatusView.render("status.json", %{activity: activity, for: user}) - - accounts = - AccountView.render("accounts.json", %{ - users: participation.conversation.users, - as: :user - }) - - %{ - id: participation.id |> to_string(), - accounts: accounts, - unread: !participation.read, - last_status: last_status - } + ConversationView.render("participation.json", %{participation: participation, user: user}) end) conn @@ -1617,31 +1603,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do with %Participation{} = participation <- Repo.get_by(Participation, id: participation_id, user_id: user.id), {:ok, participation} <- Participation.mark_as_read(participation) do - participation = Repo.preload(participation, conversation: :users) - - accounts = - AccountView.render("accounts.json", %{ - users: participation.conversation.users, - as: :user - }) - - last_activity_id = - ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{ - "user" => user, - "blocking_user" => user - }) - - activity = Activity.get_by_id_with_object(last_activity_id) - - last_status = StatusView.render("status.json", %{activity: activity, for: user}) + participation_view = + ConversationView.render("participation.json", %{participation: participation, user: user}) conn - |> json(%{ - id: participation.id, - accounts: accounts, - unread: !participation.read, - last_status: last_status - }) + |> json(participation_view) end end diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex new file mode 100644 index 000000000..d841a840c --- /dev/null +++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex @@ -0,0 +1,38 @@ +defmodule Pleroma.Web.MastodonAPI.ConversationView do + use Pleroma.Web, :view + + alias Pleroma.Activity + alias Pleroma.Repo + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.MastodonAPI.StatusView + alias Pleroma.Web.MastodonAPI.AccountView + + def render("participation.json", %{participation: participation, user: user}) do + participation = Repo.preload(participation, conversation: :users) + + last_activity_id = + with nil <- participation.last_activity_id do + ActivityPub.fetch_latest_activity_id_for_context(participation.conversation.ap_id, %{ + "user" => user, + "blocking_user" => user + }) + end + + activity = Activity.get_by_id_with_object(last_activity_id) + + last_status = StatusView.render("status.json", %{activity: activity, for: user}) + + accounts = + AccountView.render("accounts.json", %{ + users: participation.conversation.users, + as: :user + }) + + %{ + id: participation.id |> to_string(), + accounts: accounts, + unread: !participation.read, + last_status: last_status + } + end +end -- cgit v1.2.3 From eeb093631cd50583feb4864a016d0dc1e4c58f5e Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 21 Apr 2019 23:19:36 +0700 Subject: Fix Credo warning --- lib/pleroma/web/mastodon_api/views/conversation_view.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex index d841a840c..eb61baa03 100644 --- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex +++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex @@ -3,9 +3,9 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do alias Pleroma.Activity alias Pleroma.Repo + alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.MastodonAPI.StatusView - alias Pleroma.Web.MastodonAPI.AccountView def render("participation.json", %{participation: participation, user: user}) do participation = Repo.preload(participation, conversation: :users) -- cgit v1.2.3 From 4908e0eeee2ecb58b204198c20720d52548b6f4a Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sun, 21 Apr 2019 23:24:33 +0700 Subject: Fix Credo warning --- lib/pleroma/web/mastodon_api/views/conversation_view.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex index eb61baa03..8e8f7cf31 100644 --- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex +++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex @@ -3,8 +3,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do alias Pleroma.Activity alias Pleroma.Repo - alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.StatusView def render("participation.json", %{participation: participation, user: user}) do -- cgit v1.2.3 From 963d5774af7efb57fa306b3ac164049f8958a72c Mon Sep 17 00:00:00 2001 From: Sachin Joshi Date: Wed, 24 Apr 2019 07:06:17 +0545 Subject: fix the status notification with special char --- lib/pleroma/web/twitter_api/views/activity_view.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index c64152da8..1007a2a48 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -289,7 +289,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do "uri" => object.data["id"], "user" => UserView.render("show.json", %{user: user, for: opts[:for]}), "statusnet_html" => html, - "text" => text, + "text" => HtmlEntities.decode(text), "is_local" => activity.local, "is_post_verb" => true, "created_at" => created_at, -- cgit v1.2.3 From 4baea6e6d9efa619402a031a84f74787653df2b5 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Wed, 24 Apr 2019 20:01:42 +0300 Subject: Fix leaking private configuration parameters in Mastodon and Twitter APIs, and add new configuration parameters to Mastodon API This patch: - Fixes `rights` in twitterapi ignoring `show_role` - Fixes exposing default scope of the user to anyone in Mastodon API - Extends Mastodon API to be able to show and set `no_rich_text`, `default_scope`, `hide_follows`, `hide_followers`, `hide_favorites` (requested by the FE in #674) Sorry in advance for 500 line one commit diff, I should have split it up to separate MRs --- lib/pleroma/user/info.ex | 8 -- .../web/mastodon_api/mastodon_api_controller.ex | 14 ++- lib/pleroma/web/mastodon_api/views/account_view.ex | 55 +++++++--- lib/pleroma/web/twitter_api/views/user_view.ex | 112 ++++++++++++--------- 4 files changed, 115 insertions(+), 74 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 7f22a45b5..a3658d57f 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -227,14 +227,6 @@ defmodule Pleroma.User.Info do cast(info, params, [:confirmation_pending, :confirmation_token]) end - def mastodon_profile_update(info, params) do - info - |> cast(params, [ - :locked, - :banner - ]) - end - def mastodon_settings_update(info, settings) do params = %{settings: settings} diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 0ba8d9eea..1379baacf 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -35,7 +35,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.Web.OAuth.Authorization alias Pleroma.Web.OAuth.Token - import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2] + alias Pleroma.Web.ControllerHelper import Ecto.Query require Logger @@ -46,7 +46,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do action_fallback(:errors) def create_app(conn, params) do - scopes = oauth_scopes(params, ["read"]) + scopes = ControllerHelper.oauth_scopes(params, ["read"]) app_attrs = params @@ -96,8 +96,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end) info_params = - %{} - |> add_if_present(params, "locked", :locked, fn value -> {:ok, value == "true"} end) + [:no_rich_text, :locked, :hide_followers, :hide_follows, :hide_favorites, :show_role] + |> Enum.reduce(%{}, fn key, acc -> + add_if_present(acc, params, to_string(key), key, fn value -> + {:ok, ControllerHelper.truthy_param?(value)} + end) + end) |> add_if_present(params, "header", :banner, fn value -> with %Plug.Upload{} <- value, {:ok, object} <- ActivityPub.upload(value, type: :banner) do @@ -107,7 +111,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end) - info_cng = User.Info.mastodon_profile_update(user.info, info_params) + info_cng = User.Info.profile_update(user.info, info_params) with changeset <- User.update_changeset(user, user_params), changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng), diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index d87fdb15d..6e6f0ba93 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -113,21 +113,22 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do bot: bot, source: %{ note: "", - privacy: user_info.default_scope, sensitive: false }, # Pleroma extension - pleroma: - %{ - confirmation_pending: user_info.confirmation_pending, - tags: user.tags, - is_moderator: user.info.is_moderator, - is_admin: user.info.is_admin, - relationship: relationship - } - |> with_notification_settings(user, opts[:for]) + pleroma: %{ + confirmation_pending: user_info.confirmation_pending, + tags: user.tags, + hide_followers: user.info.hide_followers, + hide_follows: user.info.hide_follows, + hide_favorites: user.info.hide_favorites, + relationship: relationship + } } + |> maybe_put_role(user, opts[:for]) + |> maybe_put_settings(user, opts[:for], user_info) + |> maybe_put_notification_settings(user, opts[:for]) end defp username_from_nickname(string) when is_binary(string) do @@ -136,9 +137,37 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do defp username_from_nickname(_), do: nil - defp with_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do - Map.put(data, :notification_settings, user.info.notification_settings) + defp maybe_put_settings( + data, + %User{id: user_id} = user, + %User{id: user_id}, + user_info + ) do + data + |> Kernel.put_in([:source, :privacy], user_info.default_scope) + |> Kernel.put_in([:pleroma, :show_role], user.info.show_role) + |> Kernel.put_in([:pleroma, :no_rich_text], user.info.no_rich_text) + end + + defp maybe_put_settings(data, _, _, _), do: data + + defp maybe_put_role(data, %User{info: %{show_role: true}} = user, _) do + data + |> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin) + |> Kernel.put_in([:pleroma, :is_moderator], user.info.is_moderator) + end + + defp maybe_put_role(data, %User{id: user_id} = user, %User{id: user_id}) do + data + |> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin) + |> Kernel.put_in([:pleroma, :is_moderator], user.info.is_moderator) + end + + defp maybe_put_role(data, _, _), do: data + + defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do + Kernel.put_in(data, [:pleroma, :notification_settings], user.info.notification_settings) end - defp with_notification_settings(data, _, _), do: data + defp maybe_put_notification_settings(data, _, _), do: data end diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index 0791ed760..39b3f21c0 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -74,52 +74,48 @@ defmodule Pleroma.Web.TwitterAPI.UserView do |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end) |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end) - data = %{ - "created_at" => user.inserted_at |> Utils.format_naive_asctime(), - "description" => HTML.strip_tags((user.bio || "") |> String.replace("
", "\n")), - "description_html" => HTML.filter_tags(user.bio, User.html_filter_policy(for_user)), - "favourites_count" => 0, - "followers_count" => user_info[:follower_count], - "following" => following, - "follows_you" => follows_you, - "statusnet_blocking" => statusnet_blocking, - "friends_count" => user_info[:following_count], - "id" => user.id, - "name" => user.name || user.nickname, - "name_html" => - if(user.name, - do: HTML.strip_tags(user.name) |> Formatter.emojify(emoji), - else: user.nickname - ), - "profile_image_url" => image, - "profile_image_url_https" => image, - "profile_image_url_profile_size" => image, - "profile_image_url_original" => image, - "rights" => %{ - "delete_others_notice" => !!user.info.is_moderator, - "admin" => !!user.info.is_admin - }, - "screen_name" => user.nickname, - "statuses_count" => user_info[:note_count], - "statusnet_profile_url" => user.ap_id, - "cover_photo" => User.banner_url(user) |> MediaProxy.url(), - "background_image" => image_url(user.info.background) |> MediaProxy.url(), - "is_local" => user.local, - "locked" => user.info.locked, - "default_scope" => user.info.default_scope, - "no_rich_text" => user.info.no_rich_text, - "hide_followers" => user.info.hide_followers, - "hide_follows" => user.info.hide_follows, - "fields" => fields, - - # Pleroma extension - "pleroma" => - %{ - "confirmation_pending" => user_info.confirmation_pending, - "tags" => user.tags - } - |> maybe_with_activation_status(user, for_user) - } + data = + %{ + "created_at" => user.inserted_at |> Utils.format_naive_asctime(), + "description" => HTML.strip_tags((user.bio || "") |> String.replace("
", "\n")), + "description_html" => HTML.filter_tags(user.bio, User.html_filter_policy(for_user)), + "favourites_count" => 0, + "followers_count" => user_info[:follower_count], + "following" => following, + "follows_you" => follows_you, + "statusnet_blocking" => statusnet_blocking, + "friends_count" => user_info[:following_count], + "id" => user.id, + "name" => user.name || user.nickname, + "name_html" => + if(user.name, + do: HTML.strip_tags(user.name) |> Formatter.emojify(emoji), + else: user.nickname + ), + "profile_image_url" => image, + "profile_image_url_https" => image, + "profile_image_url_profile_size" => image, + "profile_image_url_original" => image, + "screen_name" => user.nickname, + "statuses_count" => user_info[:note_count], + "statusnet_profile_url" => user.ap_id, + "cover_photo" => User.banner_url(user) |> MediaProxy.url(), + "background_image" => image_url(user.info.background) |> MediaProxy.url(), + "is_local" => user.local, + "locked" => user.info.locked, + "hide_followers" => user.info.hide_followers, + "hide_follows" => user.info.hide_follows, + "fields" => fields, + + # Pleroma extension + "pleroma" => + %{ + "confirmation_pending" => user_info.confirmation_pending, + "tags" => user.tags + } + |> maybe_with_activation_status(user, for_user) + } + |> maybe_with_user_settings(user, for_user) data = if(user.info.is_admin || user.info.is_moderator, @@ -141,15 +137,35 @@ defmodule Pleroma.Web.TwitterAPI.UserView do defp maybe_with_activation_status(data, _, _), do: data defp maybe_with_role(data, %User{id: id} = user, %User{id: id}) do - Map.merge(data, %{"role" => role(user), "show_role" => user.info.show_role}) + Map.merge(data, %{ + "role" => role(user), + "show_role" => user.info.show_role, + "rights" => %{ + "delete_others_notice" => !!user.info.is_moderator, + "admin" => !!user.info.is_admin + } + }) end defp maybe_with_role(data, %User{info: %{show_role: true}} = user, _user) do - Map.merge(data, %{"role" => role(user)}) + Map.merge(data, %{ + "role" => role(user), + "rights" => %{ + "delete_others_notice" => !!user.info.is_moderator, + "admin" => !!user.info.is_admin + } + }) end defp maybe_with_role(data, _, _), do: data + defp maybe_with_user_settings(data, %User{info: info, id: id} = _user, %User{id: id}) do + data + |> Kernel.put_in(["default_scope"], info.default_scope) + |> Kernel.put_in(["no_rich_text"], info.no_rich_text) + end + + defp maybe_with_user_settings(data, _, _), do: data defp role(%User{info: %{:is_admin => true}}), do: "admin" defp role(%User{info: %{:is_moderator => true}}), do: "moderator" defp role(_), do: "member" -- cgit v1.2.3 From 73d01857e3ff1737a4ea733a3f6c6419379ce8e8 Mon Sep 17 00:00:00 2001 From: Alex S Date: Sun, 14 Apr 2019 19:45:56 +0700 Subject: bookmarks in separate table --- lib/pleroma/bookmark.ex | 53 ++++++++++++++++++++++ lib/pleroma/user.ex | 19 +------- .../web/mastodon_api/mastodon_api_controller.ex | 33 +++++++++++--- lib/pleroma/web/mastodon_api/views/status_view.ex | 4 +- 4 files changed, 84 insertions(+), 25 deletions(-) create mode 100644 lib/pleroma/bookmark.ex (limited to 'lib') diff --git a/lib/pleroma/bookmark.ex b/lib/pleroma/bookmark.ex new file mode 100644 index 000000000..c5c3e078b --- /dev/null +++ b/lib/pleroma/bookmark.ex @@ -0,0 +1,53 @@ +defmodule Pleroma.Bookmark do + use Ecto.Schema + + import Ecto.Changeset + import Ecto.Query + + alias Pleroma.Activity + alias Pleroma.Bookmark + alias Pleroma.FlakeId + alias Pleroma.Repo + alias Pleroma.User + + @type t :: %__MODULE__{} + + schema "bookmarks" do + belongs_to(:user, User, type: FlakeId) + belongs_to(:activity, Activity, type: FlakeId) + + timestamps() + end + + @spec create(FlakeId.t(), FlakeId.t()) :: {:ok, Bookmark.t()} | {:error, Changeset.t()} + def create(user_id, activity_id) do + attrs = %{ + user_id: user_id, + activity_id: activity_id + } + + %Bookmark{} + |> cast(attrs, [:user_id, :activity_id]) + |> validate_required([:user_id, :activity_id]) + |> unique_constraint(:activity_id, name: :bookmarks_user_id_activity_id_index) + |> Repo.insert() + end + + @spec for_user_query(FlakeId.t()) :: Ecto.Query.t() + def for_user_query(user_id) do + Bookmark + |> where(user_id: ^user_id) + |> join(:inner, [b], activity in assoc(b, :activity)) + |> preload([b, a], activity: a) + end + + @spec destroy(FlakeId.t(), FlakeId.t()) :: {:ok, Bookmark.t()} | {:error, Changeset.t()} + def destroy(user_id, activity_id) do + from(b in Bookmark, + where: b.user_id == ^user_id, + where: b.activity_id == ^activity_id + ) + |> Repo.one() + |> Repo.delete() + end +end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index f1feab279..c5b1ddc5d 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -10,6 +10,7 @@ defmodule Pleroma.User do alias Comeonin.Pbkdf2 alias Pleroma.Activity + alias Pleroma.Bookmark alias Pleroma.Formatter alias Pleroma.Notification alias Pleroma.Object @@ -53,8 +54,8 @@ defmodule Pleroma.User do field(:search_rank, :float, virtual: true) field(:search_type, :integer, virtual: true) field(:tags, {:array, :string}, default: []) - field(:bookmarks, {:array, :string}, default: []) field(:last_refreshed_at, :naive_datetime_usec) + has_many(:bookmarks, Bookmark) has_many(:notifications, Notification) has_many(:registrations, Registration) embeds_one(:info, Pleroma.User.Info) @@ -1379,22 +1380,6 @@ defmodule Pleroma.User do updated_user end - def bookmark(%User{} = user, status_id) do - bookmarks = Enum.uniq(user.bookmarks ++ [status_id]) - update_bookmarks(user, bookmarks) - end - - def unbookmark(%User{} = user, status_id) do - bookmarks = Enum.uniq(user.bookmarks -- [status_id]) - update_bookmarks(user, bookmarks) - end - - def update_bookmarks(%User{} = user, bookmarks) do - user - |> change(%{bookmarks: bookmarks}) - |> update_and_set_cache - end - defp normalize_tags(tags) do [tags] |> List.flatten() diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 0ba8d9eea..a93aa6ad5 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do use Pleroma.Web, :controller alias Ecto.Changeset alias Pleroma.Activity + alias Pleroma.Bookmark alias Pleroma.Config alias Pleroma.Filter alias Pleroma.Notification @@ -279,6 +280,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> ActivityPub.contain_timeline(user) |> Enum.reverse() + user = Repo.preload(user, :bookmarks) + conn |> add_link_headers(:home_timeline, activities) |> put_view(StatusView) @@ -297,6 +300,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> ActivityPub.fetch_public_activities() |> Enum.reverse() + user = Repo.preload(user, :bookmarks) + conn |> add_link_headers(:public_timeline, activities, false, %{"local" => local_only}) |> put_view(StatusView) @@ -304,7 +309,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do - with %User{} = user <- User.get_cached_by_id(params["id"]) do + with %User{} = user <- User.get_cached_by_id(params["id"]), + reading_user <- Repo.preload(reading_user, :bookmarks) do activities = ActivityPub.fetch_user_activities(user, reading_user, params) conn @@ -331,6 +337,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> ActivityPub.fetch_activities_query(params) |> Pagination.fetch_paginated(params) + user = Repo.preload(user, :bookmarks) + conn |> add_link_headers(:dm_timeline, activities) |> put_view(StatusView) @@ -548,7 +556,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do %Object{} = object <- Object.normalize(activity), %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), - {:ok, user} <- User.bookmark(user, object.data["id"]) do + {:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do + user = Repo.preload(user, :bookmarks) + conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) @@ -560,7 +570,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do %Object{} = object <- Object.normalize(activity), %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), - {:ok, user} <- User.unbookmark(user, object.data["id"]) do + {:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do + user = Repo.preload(user, :bookmarks) + conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) @@ -1124,15 +1136,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end - def bookmarks(%{assigns: %{user: user}} = conn, _) do + def bookmarks(%{assigns: %{user: user}} = conn, params) do user = User.get_cached_by_id(user.id) + user = Repo.preload(user, :bookmarks) + + bookmarks = + Bookmark.for_user_query(user.id) + |> Pagination.fetch_paginated(params) activities = - user.bookmarks - |> Enum.map(fn id -> Activity.get_create_by_object_ap_id(id) end) - |> Enum.reverse() + bookmarks + |> Enum.map(fn b -> b.activity end) conn + |> add_link_headers(:bookmarks, bookmarks) |> put_view(StatusView) |> render("index.json", %{activities: activities, for: user, as: :activity}) end @@ -1238,6 +1255,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> ActivityPub.fetch_activities_bounded(following, params) |> Enum.reverse() + user = Repo.preload(user, :bookmarks) + conn |> put_view(StatusView) |> render("index.json", %{activities: activities, for: user, as: :activity}) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 7dd80d708..b2ed023dc 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -148,7 +148,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || []) - bookmarked = opts[:for] && object.data["id"] in opts[:for].bookmarks + bookmarked = + opts[:for] && Ecto.assoc_loaded?(opts[:for].bookmarks) && + Enum.any?(opts[:for].bookmarks, fn b -> b.activity_id == activity.id end) attachment_data = object.data["attachment"] || [] attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment) -- cgit v1.2.3 From 324c9c8ab54b7df59534f6a8160d4a20d5b79722 Mon Sep 17 00:00:00 2001 From: Alex S Date: Sun, 14 Apr 2019 21:37:05 +0700 Subject: migrate user.bookmarks to separate table --- lib/pleroma/user.ex | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index c5b1ddc5d..3831588f9 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -55,6 +55,8 @@ defmodule Pleroma.User do field(:search_type, :integer, virtual: true) field(:tags, {:array, :string}, default: []) field(:last_refreshed_at, :naive_datetime_usec) + # TODO: add migration to delete `bookmarks` field from DB + field(:old_bookmarks, {:array, :string}, default: [], source: :bookmarks) has_many(:bookmarks, Bookmark) has_many(:notifications, Notification) has_many(:registrations, Registration) -- cgit v1.2.3 From 1258128f4fede8122a3950051e52595e36ab0511 Mon Sep 17 00:00:00 2001 From: Alex S Date: Sun, 14 Apr 2019 21:42:18 +0700 Subject: favourites add bookmark display --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index a93aa6ad5..4f8af32c3 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1093,6 +1093,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do ActivityPub.fetch_activities([], params) |> Enum.reverse() + user = Repo.preload(user, :bookmarks) + conn |> add_link_headers(:favourites, activities) |> put_view(StatusView) -- cgit v1.2.3 From 3c2ae800082f34206f95cee5fe23b5bf79ed7361 Mon Sep 17 00:00:00 2001 From: Alex S Date: Sat, 20 Apr 2019 14:41:42 +0700 Subject: unused --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 4f8af32c3..859cf9524 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -553,7 +553,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), - %Object{} = object <- Object.normalize(activity), %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), {:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do @@ -567,7 +566,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), - %Object{} = object <- Object.normalize(activity), %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), {:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do -- cgit v1.2.3 From 229ce6abbc1873c35a56450942c8aee0a027b6a8 Mon Sep 17 00:00:00 2001 From: Alex S Date: Mon, 22 Apr 2019 12:45:45 +0700 Subject: migration without using old field name removing old field from db, after bookmarks migration --- lib/pleroma/user.ex | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 3831588f9..c5b1ddc5d 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -55,8 +55,6 @@ defmodule Pleroma.User do field(:search_type, :integer, virtual: true) field(:tags, {:array, :string}, default: []) field(:last_refreshed_at, :naive_datetime_usec) - # TODO: add migration to delete `bookmarks` field from DB - field(:old_bookmarks, {:array, :string}, default: [], source: :bookmarks) has_many(:bookmarks, Bookmark) has_many(:notifications, Notification) has_many(:registrations, Registration) -- cgit v1.2.3 From 85953c0836c84550c8f61218d0a5b284c0716d34 Mon Sep 17 00:00:00 2001 From: Alex S Date: Mon, 22 Apr 2019 16:16:19 +0700 Subject: fixes for tests --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 2 +- lib/pleroma/web/mastodon_api/views/status_view.ex | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 859cf9524..f5067e17e 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -280,7 +280,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> ActivityPub.contain_timeline(user) |> Enum.reverse() - user = Repo.preload(user, :bookmarks) + user = Repo.preload(user, bookmarks: :activity) conn |> add_link_headers(:home_timeline, activities) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index b2ed023dc..57cb9fdcc 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -85,7 +85,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do activity_object = Object.normalize(activity) favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || []) - bookmarked = opts[:for] && activity_object.data["id"] in opts[:for].bookmarks + + bookmarked = + opts[:for] && Ecto.assoc_loaded?(opts[:for].bookmarks) && + Enum.any?(opts[:for].bookmarks, fn b -> + b.activity_id == activity.id or b.activity.data["object"]["id"] == object + end) mentions = activity.recipients @@ -150,7 +155,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do bookmarked = opts[:for] && Ecto.assoc_loaded?(opts[:for].bookmarks) && - Enum.any?(opts[:for].bookmarks, fn b -> b.activity_id == activity.id end) + Enum.any?(opts[:for].bookmarks, fn b -> + b.activity_id == activity.id + end) attachment_data = object.data["attachment"] || [] attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment) -- cgit v1.2.3 From a825056d4d9ef823fe45d49df01062d199518829 Mon Sep 17 00:00:00 2001 From: Alex S Date: Thu, 25 Apr 2019 14:09:57 +0700 Subject: test fixes --- .../web/mastodon_api/mastodon_api_controller.ex | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index f5067e17e..fb11abf2d 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -300,7 +300,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> ActivityPub.fetch_public_activities() |> Enum.reverse() - user = Repo.preload(user, :bookmarks) + user = Repo.preload(user, bookmarks: :activity) conn |> add_link_headers(:public_timeline, activities, false, %{"local" => local_only}) @@ -337,7 +337,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> ActivityPub.fetch_activities_query(params) |> Pagination.fetch_paginated(params) - user = Repo.preload(user, :bookmarks) + user = Repo.preload(user, bookmarks: :activity) conn |> add_link_headers(:dm_timeline, activities) @@ -348,6 +348,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.visible_for_user?(activity, user) do + user = Repo.preload(user, bookmarks: :activity) + conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user}) @@ -497,6 +499,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user), %Activity{} = announce <- Activity.normalize(announce.data) do + user = Repo.preload(user, bookmarks: :activity) + conn |> put_view(StatusView) |> try_render("status.json", %{activity: announce, for: user, as: :activity}) @@ -506,6 +510,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user), %Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do + user = Repo.preload(user, bookmarks: :activity) + conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) @@ -556,7 +562,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), {:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do - user = Repo.preload(user, :bookmarks) + user = Repo.preload(user, bookmarks: :activity) conn |> put_view(StatusView) @@ -569,7 +575,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), {:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do - user = Repo.preload(user, :bookmarks) + user = Repo.preload(user, bookmarks: :activity) conn |> put_view(StatusView) @@ -1091,7 +1097,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do ActivityPub.fetch_activities([], params) |> Enum.reverse() - user = Repo.preload(user, :bookmarks) + user = Repo.preload(user, bookmarks: :activity) conn |> add_link_headers(:favourites, activities) @@ -1138,7 +1144,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def bookmarks(%{assigns: %{user: user}} = conn, params) do user = User.get_cached_by_id(user.id) - user = Repo.preload(user, :bookmarks) + user = Repo.preload(user, bookmarks: :activity) bookmarks = Bookmark.for_user_query(user.id) @@ -1255,7 +1261,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> ActivityPub.fetch_activities_bounded(following, params) |> Enum.reverse() - user = Repo.preload(user, :bookmarks) + user = Repo.preload(user, bookmarks: :activity) conn |> put_view(StatusView) -- cgit v1.2.3 From dfc8425659620d023540538ec943490cf523f434 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 25 Apr 2019 09:14:35 +0300 Subject: Move settings to Source subentity --- lib/pleroma/web/mastodon_api/views/account_view.ex | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 6e6f0ba93..779b9a382 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -113,7 +113,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do bot: bot, source: %{ note: "", - sensitive: false + sensitive: false, + pleroma: %{} }, # Pleroma extension @@ -145,8 +146,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do ) do data |> Kernel.put_in([:source, :privacy], user_info.default_scope) - |> Kernel.put_in([:pleroma, :show_role], user.info.show_role) - |> Kernel.put_in([:pleroma, :no_rich_text], user.info.no_rich_text) + |> Kernel.put_in([:source, :pleroma, :show_role], user.info.show_role) + |> Kernel.put_in([:source, :pleroma, :no_rich_text], user.info.no_rich_text) end defp maybe_put_settings(data, _, _, _), do: data -- cgit v1.2.3 From 24c3e2db2c6846073df80c124633dc851c20c107 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 25 Apr 2019 18:11:47 -0500 Subject: Add mediaproxy whitelist capability --- lib/pleroma/web/media_proxy/media_proxy.ex | 50 ++++++++++++++++++------------ 1 file changed, 31 insertions(+), 19 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex index 3bd2affe9..5762e767b 100644 --- a/lib/pleroma/web/media_proxy/media_proxy.ex +++ b/lib/pleroma/web/media_proxy/media_proxy.ex @@ -13,32 +13,44 @@ defmodule Pleroma.Web.MediaProxy do def url(url) do config = Application.get_env(:pleroma, :media_proxy, []) + domain = URI.parse(url).host - if !Keyword.get(config, :enabled, false) or String.starts_with?(url, Pleroma.Web.base_url()) do - url - else - secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base] - - # Must preserve `%2F` for compatibility with S3 - # https://git.pleroma.social/pleroma/pleroma/issues/580 - replacement = get_replacement(url, ":2F:") - - # The URL is url-decoded and encoded again to ensure it is correctly encoded and not twice. - base64 = + cond do + !Keyword.get(config, :enabled, false) or String.starts_with?(url, Pleroma.Web.base_url()) -> url - |> String.replace("%2F", replacement) - |> URI.decode() - |> URI.encode() - |> String.replace(replacement, "%2F") - |> Base.url_encode64(@base64_opts) - sig = :crypto.hmac(:sha, secret, base64) - sig64 = sig |> Base.url_encode64(@base64_opts) + Enum.any?(Pleroma.Config.get([:media_proxy, :whitelist]), fn pattern -> + String.equivalent?(domain, pattern) + end) -> + url - build_url(sig64, base64, filename(url)) + true -> + encode_url(url) end end + def encode_url(url) do + secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base] + + # Must preserve `%2F` for compatibility with S3 + # https://git.pleroma.social/pleroma/pleroma/issues/580 + replacement = get_replacement(url, ":2F:") + + # The URL is url-decoded and encoded again to ensure it is correctly encoded and not twice. + base64 = + url + |> String.replace("%2F", replacement) + |> URI.decode() + |> URI.encode() + |> String.replace(replacement, "%2F") + |> Base.url_encode64(@base64_opts) + + sig = :crypto.hmac(:sha, secret, base64) + sig64 = sig |> Base.url_encode64(@base64_opts) + + build_url(sig64, base64, filename(url)) + end + def decode_url(sig, url) do secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base] sig = Base.url_decode64!(sig, @base64_opts) -- cgit v1.2.3 From 501af917b5a9611a4b1fabb4944b3af96b676568 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Fri, 26 Apr 2019 10:17:57 +0000 Subject: add support for bbcode --- lib/pleroma/web/common_api/utils.ex | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 887f878c4..1dfe50b40 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -182,6 +182,18 @@ defmodule Pleroma.Web.CommonAPI.Utils do end).() end + @doc """ + Formatting text as BBCode. + """ + def format_input(text, "text/bbcode", options) do + text + |> String.replace(~r/\r/, "") + |> Formatter.html_escape("text/plain") + |> BBCode.to_html() + |> (fn {:ok, html} -> html end).() + |> Formatter.linkify(options) + end + @doc """ Formatting text to html. """ -- cgit v1.2.3 From c3e9fcf098e4ec17a387fe264193dca4dda2dd77 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 27 Apr 2019 23:06:46 +0300 Subject: Fix bookmarks depending on embeded object and move checking if the status is bookmarked to SQL --- lib/pleroma/bookmark.ex | 7 +++++++ lib/pleroma/web/common_api/common_api.ex | 10 ++++++++++ lib/pleroma/web/mastodon_api/views/status_view.ex | 12 ++---------- 3 files changed, 19 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/bookmark.ex b/lib/pleroma/bookmark.ex index c5c3e078b..7f8fd43b6 100644 --- a/lib/pleroma/bookmark.ex +++ b/lib/pleroma/bookmark.ex @@ -41,6 +41,13 @@ defmodule Pleroma.Bookmark do |> preload([b, a], activity: a) end + def get(user_id, activity_id) do + Bookmark + |> where(user_id: ^user_id) + |> where(activity_id: ^activity_id) + |> Repo.one() + end + @spec destroy(FlakeId.t(), FlakeId.t()) :: {:ok, Bookmark.t()} | {:error, Changeset.t()} def destroy(user_id, activity_id) do from(b in Bookmark, diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index cfbc5dc10..ecd183110 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.CommonAPI do alias Pleroma.Activity + alias Pleroma.Bookmark alias Pleroma.Formatter alias Pleroma.Object alias Pleroma.ThreadMute @@ -282,6 +283,15 @@ defmodule Pleroma.Web.CommonAPI do end end + def bookmarked?(user, activity) do + with %Bookmark{} <- Bookmark.get(user.id, activity.id) do + true + else + _ -> + false + end + end + def report(user, data) do with {:account_id, %{"account_id" => account_id}} <- {:account_id, data}, {:account, %User{} = account} <- {:account, User.get_cached_by_id(account_id)}, diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 57cb9fdcc..62d064d71 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -86,11 +86,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do activity_object = Object.normalize(activity) favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || []) - bookmarked = - opts[:for] && Ecto.assoc_loaded?(opts[:for].bookmarks) && - Enum.any?(opts[:for].bookmarks, fn b -> - b.activity_id == activity.id or b.activity.data["object"]["id"] == object - end) + bookmarked = opts[:for] && CommonAPI.bookmarked?(opts[:for], reblogged_activity) mentions = activity.recipients @@ -153,11 +149,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || []) - bookmarked = - opts[:for] && Ecto.assoc_loaded?(opts[:for].bookmarks) && - Enum.any?(opts[:for].bookmarks, fn b -> - b.activity_id == activity.id - end) + bookmarked = opts[:for] && CommonAPI.bookmarked?(opts[:for], activity) attachment_data = object.data["attachment"] || [] attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment) -- cgit v1.2.3 From ce3789e39ab0b63e634d583cbafbda7a9e4d7550 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 27 Apr 2019 23:55:54 +0300 Subject: Add default_scope to /api/v1/update_credentials --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 9179a2682..811a45c79 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -103,6 +103,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do {:ok, ControllerHelper.truthy_param?(value)} end) end) + |> add_if_present(params, "default_scope", :default_scope) |> add_if_present(params, "header", :banner, fn value -> with %Plug.Upload{} <- value, {:ok, object} <- ActivityPub.upload(value, type: :banner) do -- cgit v1.2.3 From 61ca2f7a4ed1bfa5b5ae4da5b28ca3c546b1e141 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sun, 28 Apr 2019 19:42:43 +0300 Subject: Fix `rights` in TwitterAPI's user entity being present only for Admins/Moderators In !1093 I reused `maybe_with_role` for `rights` object, however I missed that `maybe_with_role` is called only for admins/moderators. --- lib/pleroma/web/twitter_api/views/user_view.ex | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index 39b3f21c0..ea015b8f0 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -116,12 +116,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do |> maybe_with_activation_status(user, for_user) } |> maybe_with_user_settings(user, for_user) - - data = - if(user.info.is_admin || user.info.is_moderator, - do: maybe_with_role(data, user, for_user), - else: data - ) + |> maybe_with_role(user, for_user) if assigns[:token] do Map.put(data, "token", token_string(assigns[:token])) -- cgit v1.2.3 From b0951a884914a06d283b55ed65c6322e6e4d27ae Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 29 Apr 2019 18:15:30 +0200 Subject: WebPush: Use Object.normalize, rewrite tests so they test reality. --- lib/pleroma/web/push/impl.ex | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 2233480c5..35d3ff07c 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -21,8 +21,10 @@ defmodule Pleroma.Web.Push.Impl do @doc "Performs sending notifications for user subscriptions" @spec perform(Notification.t()) :: list(any) | :error def perform( - %{activity: %{data: %{"type" => activity_type}, id: activity_id}, user_id: user_id} = - notif + %{ + activity: %{data: %{"type" => activity_type}, id: activity_id} = activity, + user_id: user_id + } = notif ) when activity_type in @types do actor = User.get_cached_by_ap_id(notif.activity.data["actor"]) @@ -30,13 +32,14 @@ defmodule Pleroma.Web.Push.Impl do type = Activity.mastodon_notification_type(notif.activity) gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key) avatar_url = User.avatar_url(actor) + object = Object.normalize(activity) for subscription <- fetch_subsriptions(user_id), get_in(subscription.data, ["alerts", type]) do %{ title: format_title(notif), access_token: subscription.token.token, - body: format_body(notif, actor), + body: format_body(notif, actor, object), notification_id: notif.id, notification_type: type, icon: avatar_url, @@ -95,25 +98,25 @@ defmodule Pleroma.Web.Push.Impl do end def format_body( - %{activity: %{data: %{"type" => "Create", "object" => %{"content" => content}}}}, - actor + %{activity: %{data: %{"type" => "Create"}}}, + actor, + %{data: %{"content" => content}} ) do "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}" end def format_body( - %{activity: %{data: %{"type" => "Announce", "object" => activity_id}}}, - actor + %{activity: %{data: %{"type" => "Announce"}}}, + actor, + %{data: %{"content" => content}} ) do - %Activity{data: %{"object" => %{"id" => object_id}}} = Activity.get_by_ap_id(activity_id) - %Object{data: %{"content" => content}} = Object.get_by_ap_id(object_id) - "@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}" end def format_body( %{activity: %{data: %{"type" => type}}}, - actor + actor, + _object ) when type in ["Follow", "Like"] do case type do -- cgit v1.2.3 From ce4825c1dc8a2b7ec2712170f45cde0ae14b46cf Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 30 Apr 2019 20:21:28 +0300 Subject: Do not normalize objects in stream_out unless the activity type is Create Saves quite a bit of time with delete activities because they would always query the db --- lib/pleroma/web/activity_pub/activity_pub.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 604ffae7b..483a2153f 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -168,7 +168,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do public = "https://www.w3.org/ns/activitystreams#Public" if activity.data["type"] in ["Create", "Announce", "Delete"] do - object = Object.normalize(activity) Pleroma.Web.Streamer.stream("user", activity) Pleroma.Web.Streamer.stream("list", activity) @@ -180,6 +179,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end if activity.data["type"] in ["Create"] do + object = Object.normalize(activity) + object.data |> Map.get("tag", []) |> Enum.filter(fn tag -> is_bitstring(tag) end) -- cgit v1.2.3 From 85fa2fbce4ee315a15b517fae4bc9b5474d1db5a Mon Sep 17 00:00:00 2001 From: Sachin Joshi Date: Wed, 1 May 2019 01:37:17 +0545 Subject: add scrubber for html special char --- lib/pleroma/html.ex | 27 ++++++++++++++++++---- lib/pleroma/web/twitter_api/views/activity_view.ex | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index cf6c0ee0a..eb33d12d9 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -28,12 +28,18 @@ defmodule Pleroma.HTML do def filter_tags(html), do: filter_tags(html, nil) def strip_tags(html), do: Scrubber.scrub(html, Scrubber.StripTags) - def get_cached_scrubbed_html_for_activity(content, scrubbers, activity, key \\ "") do + def get_cached_scrubbed_html_for_activity( + content, + scrubbers, + activity, + key \\ "", + callback \\ fn x -> x end + ) do key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}" Cachex.fetch!(:scrubber_cache, key, fn _key -> object = Pleroma.Object.normalize(activity) - ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false) + ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false, callback) end) end @@ -42,16 +48,27 @@ defmodule Pleroma.HTML do content, HtmlSanitizeEx.Scrubber.StripTags, activity, - key + key, + &HtmlEntities.decode/1 ) end def ensure_scrubbed_html( content, scrubbers, - false = _fake + fake, + callback ) do - {:commit, filter_tags(content, scrubbers)} + content = + content + |> filter_tags(scrubbers) + |> callback.() + + if fake do + {:ignore, content} + else + {:commit, content} + end end def ensure_scrubbed_html( diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index 1007a2a48..c64152da8 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -289,7 +289,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do "uri" => object.data["id"], "user" => UserView.render("show.json", %{user: user, for: opts[:for]}), "statusnet_html" => html, - "text" => HtmlEntities.decode(text), + "text" => text, "is_local" => activity.local, "is_post_verb" => true, "created_at" => created_at, -- cgit v1.2.3 From f11e7037c21d611cddd7f2eab64ebfc39630a078 Mon Sep 17 00:00:00 2001 From: Alex S Date: Wed, 1 May 2019 16:09:53 +0700 Subject: test fixes --- lib/mix/tasks/pleroma/user.ex | 2 +- lib/pleroma/user.ex | 18 +++++++++--------- .../web/activity_pub/activity_pub_controller.ex | 2 +- lib/pleroma/web/activity_pub/transmogrifier.ex | 2 +- .../web/mastodon_api/mastodon_api_controller.ex | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index b396ff0de..9e2523b18 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -126,7 +126,7 @@ defmodule Mix.Tasks.Pleroma.User do proceed? = assume_yes? or Mix.shell().yes?("Continue?") - unless not proceed? do + if proceed? do Common.start_pleroma() params = %{ diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 755d8f773..1c62f238e 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -513,7 +513,7 @@ defmodule Pleroma.User do case user_result do {:ok, user} -> {:commit, user} - {:error, error} -> {:ignore, error} + {:error, _error} -> {:ignore, nil} end end) end @@ -563,7 +563,7 @@ defmodule Pleroma.User do {:ok, user} else - e -> {:error, e} + _e -> {:error, "not found " <> nickname} end end end @@ -1210,11 +1210,11 @@ defmodule Pleroma.User do case ap_try do {:ok, user} -> - user + {:ok, user} _ -> case OStatus.make_user(ap_id) do - {:ok, user} -> user + {:ok, user} -> {:ok, user} _ -> {:error, "Could not fetch by AP id"} end end @@ -1229,15 +1229,15 @@ defmodule Pleroma.User do # Whether to fetch initial posts for the user (if it's a new user & the fetching is enabled) should_fetch_initial = is_nil(user) and Pleroma.Config.get([:fetch_initial_posts, :enabled]) + resp = fetch_by_ap_id(ap_id) + if should_fetch_initial do - with {:ok, %User{} = user} = fetch_by_ap_id(ap_id) do + with {:ok, %User{} = user} = resp do {:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user]) end - - {:ok, user} - else - _ -> {:error, "Could not fetch by AP id"} end + + resp end end diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 0b80566bf..c967ab7a9 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -155,7 +155,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do with %User{} = recipient <- User.get_cached_by_nickname(nickname), - %User{} = actor <- User.get_or_fetch_by_ap_id(params["actor"]), + {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(params["actor"]), true <- Utils.recipient_in_message(recipient, actor, params), params <- Utils.maybe_splice_recipient(recipient.ap_id, params) do Federator.incoming_ap_doc(params) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 80317171d..b774c2afa 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -126,7 +126,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_implicit_addressing(object, _), do: object def fix_addressing(object) do - %User{} = user = User.get_or_fetch_by_ap_id(object["actor"]) + {:ok, %User{} = user} = User.get_or_fetch_by_ap_id(object["actor"]) followers_collection = User.ap_followers(user) object diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 727e1c310..ed585098a 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1653,7 +1653,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do x, "id", case User.get_or_fetch(x["acct"]) do - {:ok, %User{} = %{id: id}} -> id + {:ok, %User{id: id}} -> id _ -> 0 end ) -- cgit v1.2.3 From 51e26f14f7fc342c23fe1fe643a1cf444ef9392b Mon Sep 17 00:00:00 2001 From: rinpatch Date: Wed, 1 May 2019 13:52:44 +0300 Subject: Remove redundant ensure_scrubbed_html It is never used as handling for fake and non-fake activities was merged into one function above it --- lib/pleroma/html.ex | 8 -------- 1 file changed, 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index eb33d12d9..726c370ad 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -71,14 +71,6 @@ defmodule Pleroma.HTML do end end - def ensure_scrubbed_html( - content, - scrubbers, - true = _fake - ) do - {:ignore, filter_tags(content, scrubbers)} - end - defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do generate_scrubber_signature([scrubber]) end -- cgit v1.2.3 From c854bff8f528b4f1560707ac3aa74f4a37044f52 Mon Sep 17 00:00:00 2001 From: Maksim Date: Wed, 1 May 2019 13:28:04 +0000 Subject: Refactored Pleroma.Web.Auth.Authenticator --- lib/pleroma/web/auth/authenticator.ex | 26 ++++++++++++++++++ lib/pleroma/web/auth/ldap_authenticator.ex | 39 +++++++++++---------------- lib/pleroma/web/auth/pleroma_authenticator.ex | 15 ++++------- 3 files changed, 47 insertions(+), 33 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/auth/authenticator.ex b/lib/pleroma/web/auth/authenticator.ex index b02f595dc..d4e0ffa80 100644 --- a/lib/pleroma/web/auth/authenticator.ex +++ b/lib/pleroma/web/auth/authenticator.ex @@ -42,4 +42,30 @@ defmodule Pleroma.Web.Auth.Authenticator do implementation().oauth_consumer_template() || Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html") end + + @doc "Gets user by nickname or email for auth." + @spec fetch_user(String.t()) :: User.t() | nil + def fetch_user(name) do + User.get_by_nickname_or_email(name) + end + + # Gets name and password from conn + # + @spec fetch_credentials(Plug.Conn.t() | map()) :: + {:ok, {name :: any, password :: any}} | {:error, :invalid_credentials} + def fetch_credentials(%Plug.Conn{params: params} = _), + do: fetch_credentials(params) + + def fetch_credentials(params) do + case params do + %{"authorization" => %{"name" => name, "password" => password}} -> + {:ok, {name, password}} + + %{"grant_type" => "password", "username" => name, "password" => password} -> + {:ok, {name, password}} + + _ -> + {:error, :invalid_credentials} + end + end end diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex index 363c99597..177c05636 100644 --- a/lib/pleroma/web/auth/ldap_authenticator.ex +++ b/lib/pleroma/web/auth/ldap_authenticator.ex @@ -7,6 +7,9 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do require Logger + import Pleroma.Web.Auth.Authenticator, + only: [fetch_credentials: 1, fetch_user: 1] + @behaviour Pleroma.Web.Auth.Authenticator @base Pleroma.Web.Auth.PleromaAuthenticator @@ -20,30 +23,20 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do defdelegate oauth_consumer_template, to: @base def get_user(%Plug.Conn{} = conn) do - if Pleroma.Config.get([:ldap, :enabled]) do - {name, password} = - case conn.params do - %{"authorization" => %{"name" => name, "password" => password}} -> - {name, password} - - %{"grant_type" => "password", "username" => name, "password" => password} -> - {name, password} - end - - case ldap_user(name, password) do - %User{} = user -> - {:ok, user} + with {:ldap, true} <- {:ldap, Pleroma.Config.get([:ldap, :enabled])}, + {:ok, {name, password}} <- fetch_credentials(conn), + %User{} = user <- ldap_user(name, password) do + {:ok, user} + else + {:error, {:ldap_connection_error, _}} -> + # When LDAP is unavailable, try default authenticator + @base.get_user(conn) - {:error, {:ldap_connection_error, _}} -> - # When LDAP is unavailable, try default authenticator - @base.get_user(conn) + {:ldap, _} -> + @base.get_user(conn) - error -> - error - end - else - # Fall back to default authenticator - @base.get_user(conn) + error -> + error end end @@ -94,7 +87,7 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do case :eldap.simple_bind(connection, "#{uid}=#{name},#{base}", password) do :ok -> - case User.get_by_nickname_or_email(name) do + case fetch_user(name) do %User{} = user -> user diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex index d647f1e05..dd79cdcf7 100644 --- a/lib/pleroma/web/auth/pleroma_authenticator.ex +++ b/lib/pleroma/web/auth/pleroma_authenticator.ex @@ -8,19 +8,14 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do alias Pleroma.Repo alias Pleroma.User + import Pleroma.Web.Auth.Authenticator, + only: [fetch_credentials: 1, fetch_user: 1] + @behaviour Pleroma.Web.Auth.Authenticator def get_user(%Plug.Conn{} = conn) do - {name, password} = - case conn.params do - %{"authorization" => %{"name" => name, "password" => password}} -> - {name, password} - - %{"grant_type" => "password", "username" => name, "password" => password} -> - {name, password} - end - - with {_, %User{} = user} <- {:user, User.get_by_nickname_or_email(name)}, + with {:ok, {name, password}} <- fetch_credentials(conn), + {_, %User{} = user} <- {:user, fetch_user(name)}, {_, true} <- {:checkpw, Pbkdf2.checkpw(password, user.password_hash)} do {:ok, user} else -- cgit v1.2.3 From 8af55728e47f9f62d237704cd5a33fba5f946fa2 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Mon, 29 Apr 2019 03:44:04 +0700 Subject: Fix tests --- lib/pleroma/conversation.ex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index d9c84cb1b..e6a4ccc85 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -47,9 +47,10 @@ defmodule Pleroma.Conversation do """ def create_or_bump_for(activity) do with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), + object <- Pleroma.Object.normalize(activity), "Create" <- activity.data["type"], - "Note" <- activity.data["object"]["type"], - ap_id when is_binary(ap_id) <- activity.data["object"]["context"] do + "Note" <- object.data["type"], + ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do {:ok, conversation} = create_for_ap_id(ap_id) users = User.get_users_from_set(activity.recipients, false) -- cgit v1.2.3 From 533d8cd5816343ccfb6e26495124416e9808554c Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Thu, 2 May 2019 21:04:00 +0900 Subject: Parse access_token from body parameters and URL parameters --- lib/pleroma/plugs/oauth_plug.ex | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/plugs/oauth_plug.ex b/lib/pleroma/plugs/oauth_plug.ex index 5888d596a..9d43732eb 100644 --- a/lib/pleroma/plugs/oauth_plug.ex +++ b/lib/pleroma/plugs/oauth_plug.ex @@ -16,6 +16,16 @@ defmodule Pleroma.Plugs.OAuthPlug do def call(%{assigns: %{user: %User{}}} = conn, _), do: conn + def call(%{params: %{"access_token" => access_token}} = conn, _) do + with {:ok, user, token_record} <- fetch_user_and_token(access_token) do + conn + |> assign(:token, token_record) + |> assign(:user, user) + else + _ -> conn + end + end + def call(conn, _) do with {:ok, token_str} <- fetch_token_str(conn), {:ok, user, token_record} <- fetch_user_and_token(token_str) do -- cgit v1.2.3 From 38b79461dfe6d14eb95799013b2fdd502e73245c Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 2 May 2019 22:19:14 +0300 Subject: Fix embeded relationships in Mastodon API Currently some endpoints render accounts without for user resulting in embedded relationship being empty. It causes bugs in followers/following tab in pleroma-fe but I fixed it for other endpoints as well just in case --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index ed585098a..78dae1c64 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -704,7 +704,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end - def favourited_by(conn, %{"id" => id}) do + def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Activity{data: %{"object" => object}} <- Repo.get(Activity, id), %Object{data: %{"likes" => likes}} <- Object.normalize(object) do q = from(u in User, where: u.ap_id in ^likes) @@ -712,13 +712,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do conn |> put_view(AccountView) - |> render(AccountView, "accounts.json", %{users: users, as: :user}) + |> render(AccountView, "accounts.json", %{for: user, users: users, as: :user}) else _ -> json(conn, []) end end - def reblogged_by(conn, %{"id" => id}) do + def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Activity{data: %{"object" => object}} <- Repo.get(Activity, id), %Object{data: %{"announcements" => announces}} <- Object.normalize(object) do q = from(u in User, where: u.ap_id in ^announces) @@ -726,7 +726,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do conn |> put_view(AccountView) - |> render("accounts.json", %{users: users, as: :user}) + |> render("accounts.json", %{for: user, users: users, as: :user}) else _ -> json(conn, []) end @@ -783,7 +783,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do conn |> add_link_headers(:followers, followers, user) |> put_view(AccountView) - |> render("accounts.json", %{users: followers, as: :user}) + |> render("accounts.json", %{for: for_user, users: followers, as: :user}) end end @@ -800,7 +800,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do conn |> add_link_headers(:following, followers, user) |> put_view(AccountView) - |> render("accounts.json", %{users: followers, as: :user}) + |> render("accounts.json", %{for: for_user, users: followers, as: :user}) end end @@ -808,7 +808,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do with {:ok, follow_requests} <- User.get_follow_requests(followed) do conn |> put_view(AccountView) - |> render("accounts.json", %{users: follow_requests, as: :user}) + |> render("accounts.json", %{for: followed, users: follow_requests, as: :user}) end end @@ -1235,7 +1235,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do {:ok, users} = Pleroma.List.get_following(list) do conn |> put_view(AccountView) - |> render("accounts.json", %{users: users, as: :user}) + |> render("accounts.json", %{for: user, users: users, as: :user}) end end -- cgit v1.2.3 From 81d1aa424d65b364ec8f2aee45247e7d95e3f255 Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 3 May 2019 13:39:14 +0200 Subject: Streamer: Stream out Conversations/Participations. --- lib/pleroma/conversation.ex | 11 +++++++---- lib/pleroma/web/activity_pub/activity_pub.ex | 22 ++++++++++++++++++++- lib/pleroma/web/streamer.ex | 29 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index e6a4ccc85..6e26c5fd4 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -63,10 +63,13 @@ defmodule Pleroma.Conversation do participation end) - %{ - conversation - | participations: participations - } + {:ok, + %{ + conversation + | participations: participations + }} + else + e -> {:error, e} end end end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 28754e864..6c737d0a4 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -142,8 +142,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end) Notification.create_notifications(activity) - Conversation.create_or_bump_for(activity) + + participations = + activity + |> Conversation.create_or_bump_for() + |> get_participations() + stream_out(activity) + stream_out_participations(participations) {:ok, activity} else %Activity{} = activity -> @@ -166,6 +172,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + defp get_participations({:ok, %{participations: participations}}), do: participations + defp get_participations(_), do: [] + + def stream_out_participations(participations) do + participations = + participations + |> Repo.preload(:user) + + Enum.each(participations, fn participation -> + Pleroma.Web.Streamer.stream("participation", participation) + end) + end + def stream_out(activity) do public = "https://www.w3.org/ns/activitystreams#Public" @@ -197,6 +216,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end else + # TODO: Write test, replace with visibility test if !Enum.member?(activity.data["cc"] || [], public) && !Enum.member?( activity.data["to"], diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index 72eaf2084..b8f6663a1 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.Streamer do use GenServer require Logger + alias Pleroma.Conversation.Participation alias Pleroma.Activity alias Pleroma.Notification alias Pleroma.Object @@ -71,6 +72,15 @@ defmodule Pleroma.Web.Streamer do {:noreply, topics} end + def handle_cast(%{action: :stream, topic: "participation", item: participation}, topics) do + user_topic = "direct:#{participation.user_id}" + Logger.debug("Trying to push a conversation participation to #{user_topic}\n\n") + + push_to_socket(topics, user_topic, participation) + + {:noreply, topics} + end + def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do # filter the recipient list if the activity is not public, see #270. recipient_lists = @@ -192,6 +202,19 @@ defmodule Pleroma.Web.Streamer do |> Jason.encode!() end + def represent_conversation(%Participation{} = participation) do + %{ + event: "conversation", + payload: + Pleroma.Web.MastodonAPI.ConversationView.render("participation.json", %{ + participation: participation, + user: participation.user + }) + |> Jason.encode!() + } + |> Jason.encode!() + end + def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = item) do Enum.each(topics[topic] || [], fn socket -> # Get the current user so we have up-to-date blocks etc. @@ -214,6 +237,12 @@ defmodule Pleroma.Web.Streamer do end) end + def push_to_socket(topics, topic, %Participation{} = participation) do + Enum.each(topics[topic] || [], fn socket -> + send(socket.transport_pid, {:text, represent_conversation(participation)}) + end) + end + def push_to_socket(topics, topic, %Activity{ data: %{"type" => "Delete", "deleted_activity_id" => deleted_activity_id} }) do -- cgit v1.2.3 From a0c755cc4ac3af7ddb7e8fe91fcfc1bae9750e9b Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 3 May 2019 13:40:43 +0200 Subject: MastodonApi: Bump api level. --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index aa3f46482..754946624 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -159,7 +159,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end - @mastodon_api_level "2.5.0" + @mastodon_api_level "2.6.5" def masto_instance(conn, _params) do instance = Config.get(:instance) -- cgit v1.2.3 From acb04306b6954aac8d66a74438d0e213d93a9046 Mon Sep 17 00:00:00 2001 From: feld Date: Fri, 3 May 2019 11:45:04 +0000 Subject: Standardize construction of websocket URL This follows up on the change made in d747bd98 --- lib/pleroma/plugs/http_security_plug.ex | 2 +- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex index f701aaaa5..a476f1d49 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/plugs/http_security_plug.ex @@ -35,7 +35,7 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do defp csp_string do scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme] static_url = Pleroma.Web.Endpoint.static_url() - websocket_url = String.replace(static_url, "http", "ws") + websocket_url = Pleroma.Web.Endpoint.websocket_url() connect_src = "connect-src 'self' #{static_url} #{websocket_url}" diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index ed585098a..201a21f50 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1295,8 +1295,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do initial_state = %{ meta: %{ - streaming_api_base_url: - String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws"), + streaming_api_base_url: Pleroma.Web.Endpoint.websocket_url(), access_token: token, locale: "en", domain: Pleroma.Web.Endpoint.host(), -- cgit v1.2.3 From c42ded13a2caa02f3f5aa00accce69b183546f9e Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 3 May 2019 13:53:17 +0200 Subject: Credo fixes. --- lib/pleroma/web/streamer.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index b8f6663a1..133decfc4 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -5,8 +5,8 @@ defmodule Pleroma.Web.Streamer do use GenServer require Logger - alias Pleroma.Conversation.Participation alias Pleroma.Activity + alias Pleroma.Conversation.Participation alias Pleroma.Notification alias Pleroma.Object alias Pleroma.User -- cgit v1.2.3 From 85b5c60694e07d3bfb1f885d5fda14be6b7bade9 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 7 Feb 2019 16:41:20 +0100 Subject: Pleroma.Formatter: width/height to class=emoji --- lib/pleroma/formatter.ex | 4 +--- lib/pleroma/html.ex | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index dab8910c1..0ec6bcee0 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -113,9 +113,7 @@ defmodule Pleroma.Formatter do html = if not strip do - "#{emoji}" + "#{emoji}" else "" end diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index 726c370ad..d1da746de 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -151,6 +151,7 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do Meta.allow_tag_with_these_attributes("img", [ "width", "height", + "class", "title", "alt" ]) @@ -221,6 +222,7 @@ defmodule Pleroma.HTML.Scrubber.Default do Meta.allow_tag_with_these_attributes("img", [ "width", "height", + "class", "title", "alt" ]) -- cgit v1.2.3 From d70af32127bda1431bacad58e7c6516a2e652fcd Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Mon, 11 Feb 2019 23:47:32 +0100 Subject: Pleroma.User: remove emojify on parse_bio --- lib/pleroma/user.ex | 21 +++++++++------------ .../web/mastodon_api/mastodon_api_controller.ex | 2 +- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 1c62f238e..1741ce684 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -11,7 +11,6 @@ defmodule Pleroma.User do alias Comeonin.Pbkdf2 alias Pleroma.Activity alias Pleroma.Bookmark - alias Pleroma.Formatter alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Registration @@ -1331,18 +1330,15 @@ defmodule Pleroma.User do end end - def parse_bio(bio, user \\ %User{info: %{source_data: %{}}}) - def parse_bio(nil, _user), do: "" - def parse_bio(bio, _user) when bio == "", do: bio + def parse_bio(bio) when is_binary(bio) and bio != "" do + bio + |> CommonUtils.format_input("text/plain", mentions_format: :full) + |> elem(0) + end - def parse_bio(bio, user) do - emoji = - (user.info.source_data["tag"] || []) - |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end) - |> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} -> - {String.trim(name, ":"), url} - end) + def parse_bio(_), do: "" + def parse_bio(bio, user) when is_binary(bio) and bio != "" do # TODO: get profile URLs other than user.ap_id profile_urls = [user.ap_id] @@ -1352,9 +1348,10 @@ defmodule Pleroma.User do rel: &RelMe.maybe_put_rel_me(&1, profile_urls) ) |> elem(0) - |> Formatter.emojify(emoji) end + def parse_bio(_, _), do: "" + def tag(user_identifiers, tags) when is_list(user_identifiers) do Repo.transaction(fn -> for user_identifier <- user_identifiers, do: tag(user_identifier, tags) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 201a21f50..564dbb829 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -86,7 +86,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do user_params = %{} |> add_if_present(params, "display_name", :name) - |> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value)} end) + |> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value, user)} end) |> add_if_present(params, "avatar", :avatar, fn value -> with %Plug.Upload{} <- value, {:ok, object} <- ActivityPub.upload(value, type: :avatar) do -- cgit v1.2.3 From 2f76a40d028c45e99425b061298a1b05e4b59923 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 12 Feb 2019 14:59:34 +0100 Subject: formatter.ex: Add get_emoji_map/1 --- lib/pleroma/formatter.ex | 11 +++++++++++ lib/pleroma/user/info.ex | 1 + lib/pleroma/web/activity_pub/transmogrifier.ex | 14 ++++++++++++-- lib/pleroma/web/activity_pub/views/user_view.ex | 7 ++++++- lib/pleroma/web/common_api/common_api.ex | 9 +++------ lib/pleroma/web/twitter_api/twitter_api_controller.ex | 12 +++++++++++- 6 files changed, 44 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index 0ec6bcee0..3d7c36d21 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -128,12 +128,23 @@ defmodule Pleroma.Formatter do def demojify(text, nil), do: text + @doc "Outputs a list of the emoji-shortcodes in a text" def get_emoji(text) when is_binary(text) do Enum.filter(Emoji.get_all(), fn {emoji, _, _} -> String.contains?(text, ":#{emoji}:") end) end def get_emoji(_), do: [] + @doc "Outputs a list of the emoji-Maps in a text" + def get_emoji_map(text) when is_binary(text) do + get_emoji(text) + |> Enum.reduce(%{}, fn {name, file, _group}, acc -> + Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}") + end) + end + + def get_emoji_map(_), do: [] + def html_escape({text, mentions, hashtags}, type) do {html_escape(text, type), mentions, hashtags} end diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index a3658d57f..1b81619ce 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -41,6 +41,7 @@ defmodule Pleroma.User.Info do field(:hide_favorites, :boolean, default: true) field(:pinned_activities, {:array, :string}, default: []) field(:flavour, :string, default: nil) + field(:emoji, {:array, :map}, default: []) field(:notification_settings, :map, default: %{"remote" => true, "local" => true, "followers" => true, "follows" => true} diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index b774c2afa..508f3532f 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -856,10 +856,16 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> Map.put("tag", tags ++ mentions) end + def add_emoji_tags(%User{info: %{"emoji" => _emoji} = user_info} = object) do + user_info = add_emoji_tags(user_info) + + object + |> Map.put(:info, user_info) + end + # TODO: we should probably send mtime instead of unix epoch time for updated - def add_emoji_tags(object) do + def add_emoji_tags(%{"emoji" => emoji} = object) do tags = object["tag"] || [] - emoji = object["emoji"] || [] out = emoji @@ -877,6 +883,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> Map.put("tag", tags ++ out) end + def add_emoji_tags(object) do + object + end + def set_conversation(object) do Map.put(object, "conversation", object["context"]) end diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 5926a3294..1254fdf6c 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -69,6 +69,11 @@ defmodule Pleroma.Web.ActivityPub.UserView do endpoints = render("endpoints.json", %{user: user}) + user_tags = + user + |> Transmogrifier.add_emoji_tags() + |> Map.get("tag", []) + %{ "id" => user.ap_id, "type" => "Person", @@ -87,7 +92,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do "publicKeyPem" => public_key }, "endpoints" => endpoints, - "tag" => user.info.source_data["tag"] || [] + "tag" => (user.info.source_data["tag"] || []) ++ user_tags } |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user)) |> Map.merge(maybe_make_image(&User.banner_url/2, "image", user)) diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index ecd183110..b53869c75 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -151,8 +151,8 @@ defmodule Pleroma.Web.CommonAPI do ), {to, cc} <- to_for_user_and_mentions(user, mentions, in_reply_to, visibility), context <- make_context(in_reply_to), - cw <- data["spoiler_text"], - full_payload <- String.trim(status <> (data["spoiler_text"] || "")), + cw <- data["spoiler_text"] || "", + full_payload <- String.trim(status <> cw), length when length in 1..limit <- String.length(full_payload), object <- make_note_data( @@ -170,10 +170,7 @@ defmodule Pleroma.Web.CommonAPI do Map.put( object, "emoji", - (Formatter.get_emoji(status) ++ Formatter.get_emoji(data["spoiler_text"])) - |> Enum.reduce(%{}, fn {name, file, _}, acc -> - Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}") - end) + Formatter.get_emoji_map(full_payload) ) do res = ActivityPub.create( diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 79ed9dad2..261cc4462 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do alias Ecto.Changeset alias Pleroma.Activity + alias Pleroma.Formatter alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Repo @@ -653,7 +654,16 @@ defmodule Pleroma.Web.TwitterAPI.Controller do defp parse_profile_bio(user, params) do if bio = params["description"] do - Map.put(params, "bio", User.parse_bio(bio, user)) + user_info = + user.info + |> Map.put( + "emojis", + Formatter.get_emoji_map(params["description"]) + ) + + params + |> Map.put("bio", User.parse_bio(bio, user)) + |> Map.put("info", user_info) else params end -- cgit v1.2.3 From 85434669bb0d363528b738a6d32dd9ce18401065 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 22 Feb 2019 22:35:47 +0100 Subject: Web.TwitterAPI.UserView: Also view local user emojis --- lib/pleroma/web/twitter_api/views/user_view.ex | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index ea015b8f0..6857055c9 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -67,6 +67,8 @@ defmodule Pleroma.Web.TwitterAPI.UserView do {String.trim(name, ":"), url} end) + emoji = Enum.dedup(emoji ++ user.info.emoji) + # ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``. # For example: [{"name": "Pronoun", "value": "she/her"}, …] fields = -- cgit v1.2.3 From 46bbf9e1cff4e00f2fbd95dcc21c038d7d686b86 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Sat, 23 Feb 2019 00:09:11 +0100 Subject: TwitterAPI: profile update with emoji_map --- lib/pleroma/web/twitter_api/twitter_api_controller.ex | 10 ++++++++-- lib/pleroma/web/twitter_api/views/user_view.ex | 7 ++++++- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 261cc4462..ef7b6fe65 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -654,11 +654,17 @@ defmodule Pleroma.Web.TwitterAPI.Controller do defp parse_profile_bio(user, params) do if bio = params["description"] do + emojis_text = (params["description"] || "") <> " " <> (params["name"] || "") + + emojis = + ((user.info.emoji || []) ++ Formatter.get_emoji_map(emojis_text)) + |> Enum.dedup() + user_info = user.info |> Map.put( - "emojis", - Formatter.get_emoji_map(params["description"]) + "emoji", + emojis ) params diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index 6857055c9..f0a4ddbd3 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -69,6 +69,11 @@ defmodule Pleroma.Web.TwitterAPI.UserView do emoji = Enum.dedup(emoji ++ user.info.emoji) + description_html = + (user.bio || "") + |> HTML.filter_tags(User.html_filter_policy(for_user)) + |> Formatter.emojify(emoji) + # ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``. # For example: [{"name": "Pronoun", "value": "she/her"}, …] fields = @@ -80,7 +85,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do %{ "created_at" => user.inserted_at |> Utils.format_naive_asctime(), "description" => HTML.strip_tags((user.bio || "") |> String.replace("
", "\n")), - "description_html" => HTML.filter_tags(user.bio, User.html_filter_policy(for_user)), + "description_html" => description_html, "favourites_count" => 0, "followers_count" => user_info[:follower_count], "following" => following, -- cgit v1.2.3 From b5ad1715b2d4a2a5bdaefa2d56bde71120d23acb Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Sat, 23 Feb 2019 00:57:42 +0100 Subject: MastoAPI: profile update with emoji_map --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 564dbb829..0840c2c5a 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.Bookmark alias Pleroma.Config alias Pleroma.Filter + alias Pleroma.Formatter alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Object.Fetcher @@ -96,6 +97,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end) + emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "") + + user_info_emojis = + ((user.info.emoji || []) ++ Formatter.get_emoji_map(emojis_text)) + |> Enum.dedup() + info_params = [:no_rich_text, :locked, :hide_followers, :hide_follows, :hide_favorites, :show_role] |> Enum.reduce(%{}, fn key, acc -> @@ -112,6 +119,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do _ -> :error end end) + |> Map.put(:emoji, user_info_emojis) info_cng = User.Info.profile_update(user.info, info_params) -- cgit v1.2.3 From 0e37fddd5abad5db0378c8186513fbba0fbd6586 Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 3 May 2019 19:15:55 +0200 Subject: Search: Add fts index on objects table. --- lib/mix/tasks/benchmark.ex | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 lib/mix/tasks/benchmark.ex (limited to 'lib') diff --git a/lib/mix/tasks/benchmark.ex b/lib/mix/tasks/benchmark.ex new file mode 100644 index 000000000..0fbb4dbb1 --- /dev/null +++ b/lib/mix/tasks/benchmark.ex @@ -0,0 +1,25 @@ +defmodule Mix.Tasks.Pleroma.Benchmark do + use Mix.Task + alias Mix.Tasks.Pleroma.Common + + def run(["search"]) do + Common.start_pleroma() + + Benchee.run(%{ + "search" => fn -> + Pleroma.Web.MastodonAPI.MastodonAPIController.status_search(nil, "cofe") + end + }) + end + + def run(["tag"]) do + Common.start_pleroma() + + Benchee.run(%{ + "tag" => fn -> + %{"type" => "Create", "tag" => "cofe"} + |> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities() + end + }) + end +end -- cgit v1.2.3 From 4c76f49e60287e6618f87a2bae3e0d4e8c444895 Mon Sep 17 00:00:00 2001 From: lain Date: Sat, 4 May 2019 15:06:18 +0200 Subject: BBS: small fixes. --- lib/pleroma/bbs/handler.ex | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index 1ebba77d2..75ba35dc2 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -1,12 +1,8 @@ defmodule Pleroma.BBS.Handler do - @moduledoc """ - An example implementation of `Sshd.ShellHandler`, implementing a very simple - Read-Eval-Loop, that does nothing. - """ use Sshd.ShellHandler + alias Pleroma.Activity alias Pleroma.Web.CommonAPI alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Activity def on_shell(username, _pubkey, _ip, _port) do :ok = IO.puts("Welcome to #{Pleroma.Config.get([:instance, :name])}!") -- cgit v1.2.3 From c9d1cb2dcefb850357b953a7a2d308de39b35e07 Mon Sep 17 00:00:00 2001 From: lain Date: Sat, 4 May 2019 15:08:07 +0200 Subject: BBS: Use cached user fetcher. --- lib/pleroma/bbs/handler.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index 75ba35dc2..14e6a6807 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -6,7 +6,7 @@ defmodule Pleroma.BBS.Handler do def on_shell(username, _pubkey, _ip, _port) do :ok = IO.puts("Welcome to #{Pleroma.Config.get([:instance, :name])}!") - user = Pleroma.User.get_by_nickname(to_string(username)) + user = Pleroma.User.get_cached_by_nickname(to_string(username)) Logger.debug("#{inspect(user)}") loop(run_state(user: user)) end -- cgit v1.2.3 From eb0fb73ddbed109ca4dcd758b60a25ff0dafc883 Mon Sep 17 00:00:00 2001 From: lain Date: Sat, 4 May 2019 15:47:50 +0200 Subject: BBS: Credo fixes. --- lib/pleroma/bbs/handler.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index 14e6a6807..106fe5d18 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -1,8 +1,8 @@ defmodule Pleroma.BBS.Handler do use Sshd.ShellHandler alias Pleroma.Activity - alias Pleroma.Web.CommonAPI alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.CommonAPI def on_shell(username, _pubkey, _ip, _port) do :ok = IO.puts("Welcome to #{Pleroma.Config.get([:instance, :name])}!") -- cgit v1.2.3 From af62ace9545e60a0c93498ec3054697e781e058a Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Mon, 6 May 2019 04:28:04 +0200 Subject: Add short documentation on every MRF Policy --- lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex | 2 ++ lib/pleroma/web/activity_pub/mrf/drop_policy.ex | 1 + lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex | 1 + lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex | 2 ++ lib/pleroma/web/activity_pub/mrf/keyword_policy.ex | 2 ++ lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex | 1 + lib/pleroma/web/activity_pub/mrf/noop_policy.ex | 1 + lib/pleroma/web/activity_pub/mrf/normalize_markup.ex | 1 + lib/pleroma/web/activity_pub/mrf/reject_non_public.ex | 1 + lib/pleroma/web/activity_pub/mrf/simple_policy.ex | 1 + lib/pleroma/web/activity_pub/mrf/tag_policy.ex | 1 + lib/pleroma/web/activity_pub/mrf/user_allowlist.ex | 1 + 12 files changed, 15 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex index 34665a3a6..87fa514c3 100644 --- a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex @@ -5,6 +5,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do alias Pleroma.User + @moduledoc "Prevent followbots from following with a bit of heuristic" + @behaviour Pleroma.Web.ActivityPub.MRF # XXX: this should become User.normalize_by_ap_id() or similar, really. diff --git a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex index a93ccf386..b8d38aae6 100644 --- a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do require Logger + @moduledoc "Drop and log everything received" @behaviour Pleroma.Web.ActivityPub.MRF @impl true diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex index 895376c9d..15d8514be 100644 --- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex +++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do alias Pleroma.Object + @moduledoc "Ensure a re: is prepended on replies to a post with a Subject" @behaviour Pleroma.Web.ActivityPub.MRF @reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless]) diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex index 6736f3cb9..a699f6a7e 100644 --- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex @@ -4,6 +4,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do alias Pleroma.User + @moduledoc "Block messages with too much mentions (configurable)" + @behaviour Pleroma.Web.ActivityPub.MRF defp delist_message(message, threshold) when threshold > 0 do diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex index e8dfba672..d5c341433 100644 --- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex @@ -3,6 +3,8 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do + @moduledoc "Reject or Word-Replace messages with a keyword or regex" + @behaviour Pleroma.Web.ActivityPub.MRF defp string_matches?(string, _) when not is_binary(string) do false diff --git a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex index 081456046..f30fee0d5 100644 --- a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do + @moduledoc "Ensure no content placeholder is present (such as the dot from mastodon)" @behaviour Pleroma.Web.ActivityPub.MRF @impl true diff --git a/lib/pleroma/web/activity_pub/mrf/noop_policy.ex b/lib/pleroma/web/activity_pub/mrf/noop_policy.ex index 40f37bdb1..c47cb3298 100644 --- a/lib/pleroma/web/activity_pub/mrf/noop_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/noop_policy.ex @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do + @moduledoc "Does nothing (lets the messages go through unmodified)" @behaviour Pleroma.Web.ActivityPub.MRF @impl true diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex index 3d13cdb32..9c87c6963 100644 --- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex +++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do + @moduledoc "Scrub configured hypertext markup" alias Pleroma.HTML @behaviour Pleroma.Web.ActivityPub.MRF diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex index 4197be847..ea3df1b4d 100644 --- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex +++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do alias Pleroma.User + @moduledoc "Rejects non-public (followers-only, direct) activities" @behaviour Pleroma.Web.ActivityPub.MRF @impl true diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 798ba9687..2f105700b 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do alias Pleroma.User + @moduledoc "Filter activities depending on their origin instance" @behaviour Pleroma.Web.ActivityPub.MRF defp check_accept(%{host: actor_host} = _actor_info, object) do diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex index b242e44e6..5ed1ee77c 100644 --- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do alias Pleroma.User @behaviour Pleroma.Web.ActivityPub.MRF + @moduledoc "Apply policies based on user tags" defp get_tags(%User{tags: tags}) when is_list(tags), do: tags defp get_tags(_), do: [] diff --git a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex b/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex index a3b1f8aa0..f5078d818 100644 --- a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex +++ b/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do alias Pleroma.Config + @moduledoc "Accept-list of users from specified instances" @behaviour Pleroma.Web.ActivityPub.MRF defp filter_by_list(object, []), do: {:ok, object} -- cgit v1.2.3 From e41a2f98d5f061d263cf731b76a20851931e6e33 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Mon, 6 May 2019 03:43:11 +0200 Subject: mrf/tag_policy.ex: Add some documentation mrf_tag:disable-remote-subscription exact way of working is quite unclear to me. Is it the requester that is denied if they have a tag, or is it the requestee if they have one? --- lib/pleroma/web/activity_pub/mrf/tag_policy.ex | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex index 5ed1ee77c..b52be30e7 100644 --- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex @@ -5,7 +5,19 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do alias Pleroma.User @behaviour Pleroma.Web.ActivityPub.MRF - @moduledoc "Apply policies based on user tags" + @moduledoc """ + Apply policies based on user tags + + This policy applies policies on a user activities depending on their tags + on your instance. + + - `mrf_tag:media-force-nsfw`: Mark as sensitive on presence of attachments + - `mrf_tag:media-strip`: Remove attachments + - `mrf_tag:force-unlisted`: Mark as unlisted (removes from the federated timeline) + - `mrf_tag:sandbox`: Remove from public (local and federated) timelines + - `mrf_tag:disable-remote-subscription`: Reject non-local follow requests + - `mrf_tag:disable-any-subscription`: Reject any follow requests + """ defp get_tags(%User{tags: tags}) when is_list(tags), do: tags defp get_tags(_), do: [] -- cgit v1.2.3 From 69a5074893d050a9a3ce6d245f1aa05c385ea4fb Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Mon, 6 May 2019 04:47:04 +0200 Subject: Remove H1 in @moduledoc --- lib/pleroma/object/containment.ex | 2 +- lib/pleroma/upload.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex index 25bd911fb..70325d6be 100644 --- a/lib/pleroma/object/containment.ex +++ b/lib/pleroma/object/containment.ex @@ -1,6 +1,6 @@ defmodule Pleroma.Object.Containment do @moduledoc """ - # Object Containment + help on getting the origin of contained objects This module contains some useful functions for containing objects to specific origins and determining those origins. They previously lived in the diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index f72334930..c47d65241 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -4,7 +4,7 @@ defmodule Pleroma.Upload do @moduledoc """ - # Upload + Manage user uploads Options: * `:type`: presets for activity type (defaults to Document) and size limits from app configuration -- cgit v1.2.3 From ce6ca0fefe7feb1c31447287aec117d40233652a Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Mon, 6 May 2019 16:45:22 +0000 Subject: Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into feature/845-improve-status-deletion --- lib/mix/tasks/pleroma/user.ex | 4 +-- lib/pleroma/activity.ex | 7 +++++ lib/pleroma/user.ex | 30 +++++++++++++--------- lib/pleroma/user_invite_token.ex | 2 +- .../web/twitter_api/controllers/util_controller.ex | 2 +- 5 files changed, 29 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 9e2523b18..6a83a8c0d 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -163,7 +163,7 @@ defmodule Mix.Tasks.Pleroma.User do Common.start_pleroma() with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do - User.delete(user) + User.perform(:delete, user) Mix.shell().info("User #{nickname} deleted.") else _ -> @@ -380,7 +380,7 @@ defmodule Mix.Tasks.Pleroma.User do Common.start_pleroma() with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do - User.delete_user_activities(user) + {:ok, _} = User.delete_user_activities(user) Mix.shell().info("User #{nickname} statuses deleted.") else _ -> diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 4a2ded518..73e63bb14 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -14,6 +14,8 @@ defmodule Pleroma.Activity do import Ecto.Query @type t :: %__MODULE__{} + @type actor :: String.t() + @primary_key {:id, Pleroma.FlakeId, autogenerate: true} # https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19 @@ -260,4 +262,9 @@ defmodule Pleroma.Activity do |> where([s], s.actor == ^actor) |> Repo.all() end + + @spec query_by_actor(actor()) :: Ecto.Query.t() + def query_by_actor(actor) do + from(a in Activity, where: a.actor == ^actor) + end end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 1741ce684..fd2ce81ad 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1164,7 +1164,12 @@ defmodule Pleroma.User do |> update_and_set_cache() end - def delete(%User{} = user) do + @spec delete(User.t()) :: :ok + def delete(%User{} = user), + do: PleromaJobQueue.enqueue(:background, __MODULE__, [:delete, user]) + + @spec perform(atom(), User.t()) :: {:ok, User.t()} + def perform(:delete, %User{} = user) do {:ok, user} = User.deactivate(user) # Remove all relationships @@ -1180,22 +1185,23 @@ defmodule Pleroma.User do end def delete_user_activities(%User{ap_id: ap_id} = user) do - Activity - |> where(actor: ^ap_id) - |> Activity.with_preloaded_object() - |> Repo.all() - |> Enum.each(fn - %{data: %{"type" => "Create"}} = activity -> - activity |> Object.normalize() |> ActivityPub.delete() + stream = + ap_id + |> Activity.query_by_actor() + |> Activity.with_preloaded_object() + |> Repo.stream() - # TODO: Do something with likes, follows, repeats. - _ -> - "Doing nothing" - end) + Repo.transaction(fn -> Enum.each(stream, &delete_activity(&1)) end, timeout: :infinity) {:ok, user} end + defp delete_activity(%{data: %{"type" => "Create"}} = activity) do + Object.normalize(activity) |> ActivityPub.delete() + end + + defp delete_activity(_activity), do: "Doing nothing" + def html_filter_policy(%User{info: %{no_rich_text: true}}) do Pleroma.HTML.Scrubber.TwitterText end diff --git a/lib/pleroma/user_invite_token.ex b/lib/pleroma/user_invite_token.ex index 86f0a5486..fadc89891 100644 --- a/lib/pleroma/user_invite_token.ex +++ b/lib/pleroma/user_invite_token.ex @@ -24,7 +24,7 @@ defmodule Pleroma.UserInviteToken do timestamps() end - @spec create_invite(map()) :: UserInviteToken.t() + @spec create_invite(map()) :: {:ok, UserInviteToken.t()} def create_invite(params \\ %{}) do %UserInviteToken{} |> cast(params, [:max_use, :expires_at]) diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 1122e6c5d..c03f8ab3a 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -352,7 +352,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do def delete_account(%{assigns: %{user: user}} = conn, params) do case CommonAPI.Utils.confirm_current_password(user, params["password"]) do {:ok, user} -> - Task.start(fn -> User.delete(user) end) + User.delete(user) json(conn, %{status: "success"}) {:error, msg} -> -- cgit v1.2.3 From 1040caf096347b638b9fda5b23fcccde87b32ede Mon Sep 17 00:00:00 2001 From: Maksim Date: Mon, 6 May 2019 17:51:03 +0000 Subject: fix format Modified-by: Maksim Pechnikov --- lib/pleroma/repo.ex | 28 ++++ lib/pleroma/web/oauth/app.ex | 1 + lib/pleroma/web/oauth/authorization.ex | 8 ++ lib/pleroma/web/oauth/oauth_controller.ex | 159 ++++++++++++--------- lib/pleroma/web/oauth/token.ex | 81 ++++++++--- .../web/oauth/token/strategy/refresh_token.ex | 54 +++++++ lib/pleroma/web/oauth/token/strategy/revoke.ex | 22 +++ lib/pleroma/web/oauth/token/utils.ex | 30 ++++ 8 files changed, 296 insertions(+), 87 deletions(-) create mode 100644 lib/pleroma/web/oauth/token/strategy/refresh_token.ex create mode 100644 lib/pleroma/web/oauth/token/strategy/revoke.ex create mode 100644 lib/pleroma/web/oauth/token/utils.ex (limited to 'lib') diff --git a/lib/pleroma/repo.ex b/lib/pleroma/repo.ex index aa5d427ae..f57e088bc 100644 --- a/lib/pleroma/repo.ex +++ b/lib/pleroma/repo.ex @@ -19,4 +19,32 @@ defmodule Pleroma.Repo do def init(_, opts) do {:ok, Keyword.put(opts, :url, System.get_env("DATABASE_URL"))} end + + @doc "find resource based on prepared query" + @spec find_resource(Ecto.Query.t()) :: {:ok, struct()} | {:error, :not_found} + def find_resource(%Ecto.Query{} = query) do + case __MODULE__.one(query) do + nil -> {:error, :not_found} + resource -> {:ok, resource} + end + end + + def find_resource(_query), do: {:error, :not_found} + + @doc """ + Gets association from cache or loads if need + + ## Examples + + iex> Repo.get_assoc(token, :user) + %User{} + + """ + @spec get_assoc(struct(), atom()) :: {:ok, struct()} | {:error, :not_found} + def get_assoc(resource, association) do + case __MODULE__.preload(resource, association) do + %{^association => assoc} when not is_nil(assoc) -> {:ok, assoc} + _ -> {:error, :not_found} + end + end end diff --git a/lib/pleroma/web/oauth/app.ex b/lib/pleroma/web/oauth/app.ex index 3476da484..bccc2ac96 100644 --- a/lib/pleroma/web/oauth/app.ex +++ b/lib/pleroma/web/oauth/app.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.OAuth.App do use Ecto.Schema import Ecto.Changeset + @type t :: %__MODULE__{} schema "apps" do field(:client_name, :string) field(:redirect_uris, :string) diff --git a/lib/pleroma/web/oauth/authorization.ex b/lib/pleroma/web/oauth/authorization.ex index 3461f9983..ca3901cc4 100644 --- a/lib/pleroma/web/oauth/authorization.ex +++ b/lib/pleroma/web/oauth/authorization.ex @@ -13,6 +13,7 @@ defmodule Pleroma.Web.OAuth.Authorization do import Ecto.Changeset import Ecto.Query + @type t :: %__MODULE__{} schema "oauth_authorizations" do field(:token, :string) field(:scopes, {:array, :string}, default: []) @@ -63,4 +64,11 @@ defmodule Pleroma.Web.OAuth.Authorization do ) |> Repo.delete_all() end + + @doc "gets auth for app by token" + @spec get_by_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found} + def get_by_token(%App{id: app_id} = _app, token) do + from(t in __MODULE__, where: t.app_id == ^app_id and t.token == ^token) + |> Repo.find_resource() + end end diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 688eaca11..e3c01217d 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -13,11 +13,15 @@ defmodule Pleroma.Web.OAuth.OAuthController do alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Authorization alias Pleroma.Web.OAuth.Token + alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken + alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2] if Pleroma.Config.oauth_consumer_enabled?(), do: plug(Ueberauth) + @expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600) + plug(:fetch_session) plug(:fetch_flash) @@ -138,25 +142,33 @@ defmodule Pleroma.Web.OAuth.OAuthController do Authenticator.handle_error(conn, error) end + @doc "Renew access_token with refresh_token" + def token_exchange( + conn, + %{"grant_type" => "refresh_token", "refresh_token" => token} = params + ) do + with %App{} = app <- get_app_from_request(conn, params), + {:ok, %{user: user} = token} <- Token.get_by_refresh_token(app, token), + {:ok, token} <- RefreshToken.grant(token) do + response_attrs = %{created_at: Token.Utils.format_created_at(token)} + + json(conn, response_token(user, token, response_attrs)) + else + _error -> + put_status(conn, 400) + |> json(%{error: "Invalid credentials"}) + end + end + def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do with %App{} = app <- get_app_from_request(conn, params), - fixed_token = fix_padding(params["code"]), - %Authorization{} = auth <- - Repo.get_by(Authorization, token: fixed_token, app_id: app.id), + fixed_token = Token.Utils.fix_padding(params["code"]), + {:ok, auth} <- Authorization.get_by_token(app, fixed_token), %User{} = user <- User.get_cached_by_id(auth.user_id), - {:ok, token} <- Token.exchange_token(app, auth), - {:ok, inserted_at} <- DateTime.from_naive(token.inserted_at, "Etc/UTC") do - response = %{ - token_type: "Bearer", - access_token: token.token, - refresh_token: token.refresh_token, - created_at: DateTime.to_unix(inserted_at), - expires_in: 60 * 10, - scope: Enum.join(token.scopes, " "), - me: user.ap_id - } - - json(conn, response) + {:ok, token} <- Token.exchange_token(app, auth) do + response_attrs = %{created_at: Token.Utils.format_created_at(token)} + + json(conn, response_token(user, token, response_attrs)) else _error -> put_status(conn, 400) @@ -177,16 +189,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do true <- Enum.any?(scopes), {:ok, auth} <- Authorization.create_authorization(app, user, scopes), {:ok, token} <- Token.exchange_token(app, auth) do - response = %{ - token_type: "Bearer", - access_token: token.token, - refresh_token: token.refresh_token, - expires_in: 60 * 10, - scope: Enum.join(token.scopes, " "), - me: user.ap_id - } - - json(conn, response) + json(conn, response_token(user, token)) else {:auth_active, false} -> # Per https://github.com/tootsuite/mastodon/blob/ @@ -218,10 +221,12 @@ defmodule Pleroma.Web.OAuth.OAuthController do token_exchange(conn, params) end - def token_revoke(conn, %{"token" => token} = params) do + # Bad request + def token_exchange(conn, params), do: bad_request(conn, params) + + def token_revoke(conn, %{"token" => _token} = params) do with %App{} = app <- get_app_from_request(conn, params), - %Token{} = token <- Repo.get_by(Token, token: token, app_id: app.id), - {:ok, %Token{}} <- Repo.delete(token) do + {:ok, _token} <- RevokeToken.revoke(app, params) do json(conn, %{}) else _error -> @@ -230,6 +235,15 @@ defmodule Pleroma.Web.OAuth.OAuthController do end end + def token_revoke(conn, params), do: bad_request(conn, params) + + # Response for bad request + defp bad_request(conn, _) do + conn + |> put_status(500) + |> json(%{error: "Bad request"}) + end + @doc "Prepares OAuth request to provider for Ueberauth" def prepare_request(conn, %{"provider" => provider, "authorization" => auth_attrs}) do scope = @@ -278,25 +292,22 @@ defmodule Pleroma.Web.OAuth.OAuthController do params = callback_params(params) with {:ok, registration} <- Authenticator.get_registration(conn) do - user = Repo.preload(registration, :user).user auth_attrs = Map.take(params, ~w(client_id redirect_uri scope scopes state)) - if user do - create_authorization( - conn, - %{"authorization" => auth_attrs}, - user: user - ) - else - registration_params = - Map.merge(auth_attrs, %{ - "nickname" => Registration.nickname(registration), - "email" => Registration.email(registration) - }) + case Repo.get_assoc(registration, :user) do + {:ok, user} -> + create_authorization(conn, %{"authorization" => auth_attrs}, user: user) - conn - |> put_session(:registration_id, registration.id) - |> registration_details(%{"authorization" => registration_params}) + _ -> + registration_params = + Map.merge(auth_attrs, %{ + "nickname" => Registration.nickname(registration), + "email" => Registration.email(registration) + }) + + conn + |> put_session(:registration_id, registration.id) + |> registration_details(%{"authorization" => registration_params}) end else _ -> @@ -399,36 +410,30 @@ defmodule Pleroma.Web.OAuth.OAuthController do end end - # XXX - for whatever reason our token arrives urlencoded, but Plug.Conn should be - # decoding it. Investigate sometime. - defp fix_padding(token) do - token - |> URI.decode() - |> Base.url_decode64!(padding: false) - |> Base.url_encode64(padding: false) + defp get_app_from_request(conn, params) do + conn + |> fetch_client_credentials(params) + |> fetch_client end - defp get_app_from_request(conn, params) do - # Per RFC 6749, HTTP Basic is preferred to body params - {client_id, client_secret} = - with ["Basic " <> encoded] <- get_req_header(conn, "authorization"), - {:ok, decoded} <- Base.decode64(encoded), - [id, secret] <- - String.split(decoded, ":") - |> Enum.map(fn s -> URI.decode_www_form(s) end) do - {id, secret} - else - _ -> {params["client_id"], params["client_secret"]} - end + defp fetch_client({id, secret}) when is_binary(id) and is_binary(secret) do + Repo.get_by(App, client_id: id, client_secret: secret) + end - if client_id && client_secret do - Repo.get_by( - App, - client_id: client_id, - client_secret: client_secret - ) + defp fetch_client({_id, _secret}), do: nil + + defp fetch_client_credentials(conn, params) do + # Per RFC 6749, HTTP Basic is preferred to body params + with ["Basic " <> encoded] <- get_req_header(conn, "authorization"), + {:ok, decoded} <- Base.decode64(encoded), + [id, secret] <- + Enum.map( + String.split(decoded, ":"), + fn s -> URI.decode_www_form(s) end + ) do + {id, secret} else - nil + _ -> {params["client_id"], params["client_secret"]} end end @@ -441,4 +446,16 @@ defmodule Pleroma.Web.OAuth.OAuthController do defp put_session_registration_id(conn, registration_id), do: put_session(conn, :registration_id, registration_id) + + defp response_token(%User{} = user, token, opts \\ %{}) do + %{ + token_type: "Bearer", + access_token: token.token, + refresh_token: token.refresh_token, + expires_in: @expires_in, + scope: Enum.join(token.scopes, " "), + me: user.ap_id + } + |> Map.merge(opts) + end end diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex index 399140003..4e5d1d118 100644 --- a/lib/pleroma/web/oauth/token.ex +++ b/lib/pleroma/web/oauth/token.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.OAuth.Token do use Ecto.Schema import Ecto.Query + import Ecto.Changeset alias Pleroma.Repo alias Pleroma.User @@ -13,6 +14,9 @@ defmodule Pleroma.Web.OAuth.Token do alias Pleroma.Web.OAuth.Authorization alias Pleroma.Web.OAuth.Token + @expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600) + @type t :: %__MODULE__{} + schema "oauth_tokens" do field(:token, :string) field(:refresh_token, :string) @@ -24,28 +28,67 @@ defmodule Pleroma.Web.OAuth.Token do timestamps() end + @doc "Gets token for app by access token" + @spec get_by_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found} + def get_by_token(%App{id: app_id} = _app, token) do + from(t in __MODULE__, where: t.app_id == ^app_id and t.token == ^token) + |> Repo.find_resource() + end + + @doc "Gets token for app by refresh token" + @spec get_by_refresh_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found} + def get_by_refresh_token(%App{id: app_id} = _app, token) do + from(t in __MODULE__, + where: t.app_id == ^app_id and t.refresh_token == ^token, + preload: [:user] + ) + |> Repo.find_resource() + end + def exchange_token(app, auth) do with {:ok, auth} <- Authorization.use_token(auth), true <- auth.app_id == app.id do - create_token(app, User.get_cached_by_id(auth.user_id), auth.scopes) + create_token( + app, + User.get_cached_by_id(auth.user_id), + %{scopes: auth.scopes} + ) end end - def create_token(%App{} = app, %User{} = user, scopes \\ nil) do - scopes = scopes || app.scopes - token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false) - refresh_token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false) - - token = %Token{ - token: token, - refresh_token: refresh_token, - scopes: scopes, - user_id: user.id, - app_id: app.id, - valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10) - } - - Repo.insert(token) + defp put_token(changeset) do + changeset + |> change(%{token: Token.Utils.generate_token()}) + |> validate_required([:token]) + |> unique_constraint(:token) + end + + defp put_refresh_token(changeset, attrs) do + refresh_token = Map.get(attrs, :refresh_token, Token.Utils.generate_token()) + + changeset + |> change(%{refresh_token: refresh_token}) + |> validate_required([:refresh_token]) + |> unique_constraint(:refresh_token) + end + + defp put_valid_until(changeset, attrs) do + expires_in = + Map.get(attrs, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), @expires_in)) + + changeset + |> change(%{valid_until: expires_in}) + |> validate_required([:valid_until]) + end + + def create_token(%App{} = app, %User{} = user, attrs \\ %{}) do + %__MODULE__{user_id: user.id, app_id: app.id} + |> cast(%{scopes: attrs[:scopes] || app.scopes}, [:scopes]) + |> validate_required([:scopes, :user_id, :app_id]) + |> put_valid_until(attrs) + |> put_token + |> put_refresh_token(attrs) + |> Repo.insert() end def delete_user_tokens(%User{id: user_id}) do @@ -73,4 +116,10 @@ defmodule Pleroma.Web.OAuth.Token do |> Repo.all() |> Repo.preload(:app) end + + def is_expired?(%__MODULE__{valid_until: valid_until}) do + NaiveDateTime.diff(NaiveDateTime.utc_now(), valid_until) > 0 + end + + def is_expired?(_), do: false end diff --git a/lib/pleroma/web/oauth/token/strategy/refresh_token.ex b/lib/pleroma/web/oauth/token/strategy/refresh_token.ex new file mode 100644 index 000000000..7df0be14e --- /dev/null +++ b/lib/pleroma/web/oauth/token/strategy/refresh_token.ex @@ -0,0 +1,54 @@ +defmodule Pleroma.Web.OAuth.Token.Strategy.RefreshToken do + @moduledoc """ + Functions for dealing with refresh token strategy. + """ + + alias Pleroma.Config + alias Pleroma.Repo + alias Pleroma.Web.OAuth.Token + alias Pleroma.Web.OAuth.Token.Strategy.Revoke + + @doc """ + Will grant access token by refresh token. + """ + @spec grant(Token.t()) :: {:ok, Token.t()} | {:error, any()} + def grant(token) do + access_token = Repo.preload(token, [:user, :app]) + + result = + Repo.transaction(fn -> + token_params = %{ + app: access_token.app, + user: access_token.user, + scopes: access_token.scopes + } + + access_token + |> revoke_access_token() + |> create_access_token(token_params) + end) + + case result do + {:ok, {:error, reason}} -> {:error, reason} + {:ok, {:ok, token}} -> {:ok, token} + {:error, reason} -> {:error, reason} + end + end + + defp revoke_access_token(token) do + Revoke.revoke(token) + end + + defp create_access_token({:error, error}, _), do: {:error, error} + + defp create_access_token({:ok, token}, %{app: app, user: user} = token_params) do + Token.create_token(app, user, add_refresh_token(token_params, token.refresh_token)) + end + + defp add_refresh_token(params, token) do + case Config.get([:oauth2, :issue_new_refresh_token], false) do + true -> Map.put(params, :refresh_token, token) + false -> params + end + end +end diff --git a/lib/pleroma/web/oauth/token/strategy/revoke.ex b/lib/pleroma/web/oauth/token/strategy/revoke.ex new file mode 100644 index 000000000..dea63ca54 --- /dev/null +++ b/lib/pleroma/web/oauth/token/strategy/revoke.ex @@ -0,0 +1,22 @@ +defmodule Pleroma.Web.OAuth.Token.Strategy.Revoke do + @moduledoc """ + Functions for dealing with revocation. + """ + + alias Pleroma.Repo + alias Pleroma.Web.OAuth.App + alias Pleroma.Web.OAuth.Token + + @doc "Finds and revokes access token for app and by token" + @spec revoke(App.t(), map()) :: {:ok, Token.t()} | {:error, :not_found | Ecto.Changeset.t()} + def revoke(%App{} = app, %{"token" => token} = _attrs) do + with {:ok, token} <- Token.get_by_token(app, token), + do: revoke(token) + end + + @doc "Revokes access token" + @spec revoke(Token.t()) :: {:ok, Token.t()} | {:error, Ecto.Changeset.t()} + def revoke(%Token{} = token) do + Repo.delete(token) + end +end diff --git a/lib/pleroma/web/oauth/token/utils.ex b/lib/pleroma/web/oauth/token/utils.ex new file mode 100644 index 000000000..a81560a1c --- /dev/null +++ b/lib/pleroma/web/oauth/token/utils.ex @@ -0,0 +1,30 @@ +defmodule Pleroma.Web.OAuth.Token.Utils do + @moduledoc """ + Auxiliary functions for dealing with tokens. + """ + + @doc "convert token inserted_at to unix timestamp" + def format_created_at(%{inserted_at: inserted_at} = _token) do + inserted_at + |> DateTime.from_naive!("Etc/UTC") + |> DateTime.to_unix() + end + + @doc false + @spec generate_token(keyword()) :: binary() + def generate_token(opts \\ []) do + opts + |> Keyword.get(:size, 32) + |> :crypto.strong_rand_bytes() + |> Base.url_encode64(padding: false) + end + + # XXX - for whatever reason our token arrives urlencoded, but Plug.Conn should be + # decoding it. Investigate sometime. + def fix_padding(token) do + token + |> URI.decode() + |> Base.url_decode64!(padding: false) + |> Base.url_encode64(padding: false) + end +end -- cgit v1.2.3 From e71ddf23bac6e9b92f2158ac647e6fb68677b1b0 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Tue, 7 May 2019 16:11:10 +0000 Subject: containment: remove pointless moduledoc line --- lib/pleroma/object/containment.ex | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex index 70325d6be..2f4687fa2 100644 --- a/lib/pleroma/object/containment.ex +++ b/lib/pleroma/object/containment.ex @@ -1,7 +1,5 @@ defmodule Pleroma.Object.Containment do @moduledoc """ - help on getting the origin of contained objects - This module contains some useful functions for containing objects to specific origins and determining those origins. They previously lived in the ActivityPub `Transmogrifier` module. -- cgit v1.2.3 From 06947c91471fda6e774c5b6fc04720b87db2b1e6 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 3 May 2019 22:15:47 +0300 Subject: Remove bookmarks assoc --- lib/pleroma/user.ex | 2 -- .../web/mastodon_api/mastodon_api_controller.ex | 24 +--------------------- 2 files changed, 1 insertion(+), 25 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index fd2ce81ad..b1adaad2f 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -10,7 +10,6 @@ defmodule Pleroma.User do alias Comeonin.Pbkdf2 alias Pleroma.Activity - alias Pleroma.Bookmark alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Registration @@ -54,7 +53,6 @@ defmodule Pleroma.User do field(:search_type, :integer, virtual: true) field(:tags, {:array, :string}, default: []) field(:last_refreshed_at, :naive_datetime_usec) - has_many(:bookmarks, Bookmark) has_many(:notifications, Notification) has_many(:registrations, Registration) embeds_one(:info, Pleroma.User.Info) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index be60e5e3c..2a3d58592 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -295,8 +295,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> ActivityPub.contain_timeline(user) |> Enum.reverse() - user = Repo.preload(user, bookmarks: :activity) - conn |> add_link_headers(:home_timeline, activities) |> put_view(StatusView) @@ -315,8 +313,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> ActivityPub.fetch_public_activities() |> Enum.reverse() - user = Repo.preload(user, bookmarks: :activity) - conn |> add_link_headers(:public_timeline, activities, false, %{"local" => local_only}) |> put_view(StatusView) @@ -324,8 +320,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do - with %User{} = user <- User.get_cached_by_id(params["id"]), - reading_user <- Repo.preload(reading_user, :bookmarks) do + with %User{} = user <- User.get_cached_by_id(params["id"]) do activities = ActivityPub.fetch_user_activities(user, reading_user, params) conn @@ -352,8 +347,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> ActivityPub.fetch_activities_query(params) |> Pagination.fetch_paginated(params) - user = Repo.preload(user, bookmarks: :activity) - conn |> add_link_headers(:dm_timeline, activities) |> put_view(StatusView) @@ -363,8 +356,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Activity{} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.visible_for_user?(activity, user) do - user = Repo.preload(user, bookmarks: :activity) - conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user}) @@ -514,8 +505,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user), %Activity{} = announce <- Activity.normalize(announce.data) do - user = Repo.preload(user, bookmarks: :activity) - conn |> put_view(StatusView) |> try_render("status.json", %{activity: announce, for: user, as: :activity}) @@ -525,8 +514,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do with {:ok, _unannounce, %{data: %{"id" => id}}} <- CommonAPI.unrepeat(ap_id_or_id, user), %Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do - user = Repo.preload(user, bookmarks: :activity) - conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) @@ -577,8 +564,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), {:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do - user = Repo.preload(user, bookmarks: :activity) - conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) @@ -590,8 +575,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), {:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do - user = Repo.preload(user, bookmarks: :activity) - conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) @@ -1112,8 +1095,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do ActivityPub.fetch_activities([], params) |> Enum.reverse() - user = Repo.preload(user, bookmarks: :activity) - conn |> add_link_headers(:favourites, activities) |> put_view(StatusView) @@ -1159,7 +1140,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def bookmarks(%{assigns: %{user: user}} = conn, params) do user = User.get_cached_by_id(user.id) - user = Repo.preload(user, bookmarks: :activity) bookmarks = Bookmark.for_user_query(user.id) @@ -1276,8 +1256,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do |> ActivityPub.fetch_activities_bounded(following, params) |> Enum.reverse() - user = Repo.preload(user, bookmarks: :activity) - conn |> put_view(StatusView) |> render("index.json", %{activities: activities, for: user, as: :activity}) -- cgit v1.2.3 From f841eb7cdb83afc444dfe260581b6be6e690a717 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 4 May 2019 12:46:42 +0300 Subject: Preload bookmarks wherever the object is preloaded --- lib/pleroma/activity.ex | 12 +++++++++--- lib/pleroma/web/activity_pub/activity_pub.ex | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 73e63bb14..7845c264a 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Activity do use Ecto.Schema alias Pleroma.Activity + alias Pleroma.Bookmark alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Repo @@ -36,6 +37,7 @@ defmodule Pleroma.Activity do field(:actor, :string) field(:recipients, {:array, :string}, default: []) has_many(:notifications, Notification, on_delete: :delete_all) + has_many(:bookmarks, Bookmark, on_delete: :delete_all) # Attention: this is a fake relation, don't try to preload it blindly and expect it to work! # The foreign key is embedded in a jsonb field. @@ -71,6 +73,7 @@ defmodule Pleroma.Activity do ) ) |> preload([activity, object], object: object) + |> preload(:bookmarks) end def get_by_ap_id(ap_id) do @@ -102,7 +105,8 @@ defmodule Pleroma.Activity do activity.data, activity.data ), - preload: [object: o] + preload: [object: o], + preload: :bookmarks ) ) end @@ -122,7 +126,8 @@ defmodule Pleroma.Activity do activity.data, activity.data ), - preload: [object: o] + preload: [object: o], + preload: :bookmarks ) |> Repo.one() end @@ -200,7 +205,8 @@ defmodule Pleroma.Activity do activity.data, activity.data ), - preload: [object: o] + preload: [object: o], + preload: :bookmarks ) end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 6c737d0a4..bd2544470 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -137,6 +137,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do activity end + activity = + if activity.data["type"] in ["Create", "Announce"] do + Repo.preload(activity, :bookmarks) + else + activity + end + Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end) -- cgit v1.2.3 From 3a7c14645ed726bd6b7deb6489ec0578c4d8cd79 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 4 May 2019 13:42:54 +0300 Subject: - Actually use preloaded bookmarks in views - Preload bookmarks in bookmark timeline - Rework bookmark preload tests --- lib/pleroma/activity.ex | 13 +++++++++---- lib/pleroma/bookmark.ex | 3 ++- .../web/mastodon_api/mastodon_api_controller.ex | 9 ++++++++- lib/pleroma/web/mastodon_api/views/status_view.ex | 18 +++++++++++++++--- 4 files changed, 34 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 7845c264a..e432fcb07 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -73,6 +73,11 @@ defmodule Pleroma.Activity do ) ) |> preload([activity, object], object: object) + |> with_preloaded_bookmarks() + end + + def with_preloaded_bookmarks(query) do + query |> preload(:bookmarks) end @@ -105,9 +110,9 @@ defmodule Pleroma.Activity do activity.data, activity.data ), - preload: [object: o], - preload: :bookmarks + preload: [object: o] ) + |> with_preloaded_bookmarks() ) end @@ -126,9 +131,9 @@ defmodule Pleroma.Activity do activity.data, activity.data ), - preload: [object: o], - preload: :bookmarks + preload: [object: o] ) + |> with_preloaded_bookmarks() |> Repo.one() end diff --git a/lib/pleroma/bookmark.ex b/lib/pleroma/bookmark.ex index 7f8fd43b6..bbb51c22c 100644 --- a/lib/pleroma/bookmark.ex +++ b/lib/pleroma/bookmark.ex @@ -38,7 +38,8 @@ defmodule Pleroma.Bookmark do Bookmark |> where(user_id: ^user_id) |> join(:inner, [b], activity in assoc(b, :activity)) - |> preload([b, a], activity: a) + |> join(:inner, [_b, a], bookmarks in assoc(a, :bookmarks)) + |> preload([b, a, b2], activity: {a, bookmarks: b2}) end def get(user_id, activity_id) do diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 2a3d58592..451fc85ce 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -563,7 +563,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do with %Activity{} = activity <- Activity.get_by_id_with_object(id), %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), - {:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do + {:ok, bookmark} <- Bookmark.create(user.id, activity.id) do + activity = %{activity | bookmarks: [bookmark | activity.bookmarks]} + conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) @@ -575,6 +577,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), {:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do + bookmarks = + Enum.filter(activity.bookmarks, fn %{user_id: user_id} -> user_id != user.id end) + + activity = %{activity | bookmarks: bookmarks} + conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 62d064d71..c5a7bcbab 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -80,13 +80,21 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do user = get_user(activity.data["actor"]) created_at = Utils.to_masto_date(activity.data["published"]) - reblogged_activity = Activity.get_create_by_object_ap_id(object) + reblogged_activity = + Activity.create_by_object_ap_id(object) + |> Activity.with_preloaded_bookmarks() + |> Repo.one() + reblogged = render("status.json", Map.put(opts, :activity, reblogged_activity)) activity_object = Object.normalize(activity) favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || []) - bookmarked = opts[:for] && CommonAPI.bookmarked?(opts[:for], reblogged_activity) + bookmarked = + opts[:for] && Ecto.assoc_loaded?(reblogged_activity.bookmarks) && + Enum.any?(reblogged_activity.bookmarks, fn %{user_id: user_id} -> + user_id == opts[:for].id + end) mentions = activity.recipients @@ -149,7 +157,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || []) - bookmarked = opts[:for] && CommonAPI.bookmarked?(opts[:for], activity) + bookmarked = + opts[:for] && Ecto.assoc_loaded?(activity.bookmarks) && + Enum.any?(activity.bookmarks, fn %{user_id: user_id} -> + user_id == opts[:for].id + end) attachment_data = object.data["attachment"] || [] attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment) -- cgit v1.2.3 From be067ec2ab7d3ebcbad7b1306ab19b266dcda664 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 4 May 2019 13:50:39 +0300 Subject: Use with_preloaded_bookmark in create_by_object_ap_id_with_object --- lib/pleroma/activity.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index e432fcb07..ab41364dc 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -210,9 +210,9 @@ defmodule Pleroma.Activity do activity.data, activity.data ), - preload: [object: o], - preload: :bookmarks + preload: [object: o] ) + |> with_preloaded_bookmarks() end def create_by_object_ap_id_with_object(_), do: nil -- cgit v1.2.3 From 4c5125dedc429ac021c861c11eb2e41c856ae4e8 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 7 May 2019 18:00:50 +0300 Subject: Remove `bookmarks` assoc and add a fake `bookmark` assoc instead --- lib/pleroma/activity.ex | 29 ++++++++++++++++------ lib/pleroma/bookmark.ex | 3 +-- lib/pleroma/web/activity_pub/activity_pub.ex | 15 +++++------ .../web/mastodon_api/mastodon_api_controller.ex | 11 ++------ lib/pleroma/web/mastodon_api/views/status_view.ex | 24 ++++++------------ 5 files changed, 40 insertions(+), 42 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index ab41364dc..2b661edc1 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Activity do alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Repo + alias Pleroma.User import Ecto.Changeset import Ecto.Query @@ -36,8 +37,9 @@ defmodule Pleroma.Activity do field(:local, :boolean, default: true) field(:actor, :string) field(:recipients, {:array, :string}, default: []) + # This is a fake relation, do not use outside of with_preloaded_bookmark/get_bookmark + has_one(:bookmark, Bookmark) has_many(:notifications, Notification, on_delete: :delete_all) - has_many(:bookmarks, Bookmark, on_delete: :delete_all) # Attention: this is a fake relation, don't try to preload it blindly and expect it to work! # The foreign key is embedded in a jsonb field. @@ -73,14 +75,18 @@ defmodule Pleroma.Activity do ) ) |> preload([activity, object], object: object) - |> with_preloaded_bookmarks() end - def with_preloaded_bookmarks(query) do - query - |> preload(:bookmarks) + def with_preloaded_bookmark(query, %User{} = user) do + from([a] in query, + left_join: b in Bookmark, + on: b.user_id == ^user.id and b.activity_id == a.id, + preload: [bookmark: b] + ) end + def with_preloaded_bookmark(query, _), do: query + def get_by_ap_id(ap_id) do Repo.one( from( @@ -90,6 +96,16 @@ defmodule Pleroma.Activity do ) end + def get_bookmark(%Activity{} = activity, %User{} = user) do + if Ecto.assoc_loaded?(activity.bookmark) do + activity.bookmark + else + Bookmark.get(user.id, activity.id) + end + end + + def get_bookmark(_, _), do: nil + def change(struct, params \\ %{}) do struct |> cast(params, [:data]) @@ -112,7 +128,6 @@ defmodule Pleroma.Activity do ), preload: [object: o] ) - |> with_preloaded_bookmarks() ) end @@ -133,7 +148,6 @@ defmodule Pleroma.Activity do ), preload: [object: o] ) - |> with_preloaded_bookmarks() |> Repo.one() end @@ -212,7 +226,6 @@ defmodule Pleroma.Activity do ), preload: [object: o] ) - |> with_preloaded_bookmarks() end def create_by_object_ap_id_with_object(_), do: nil diff --git a/lib/pleroma/bookmark.ex b/lib/pleroma/bookmark.ex index bbb51c22c..7f8fd43b6 100644 --- a/lib/pleroma/bookmark.ex +++ b/lib/pleroma/bookmark.ex @@ -38,8 +38,7 @@ defmodule Pleroma.Bookmark do Bookmark |> where(user_id: ^user_id) |> join(:inner, [b], activity in assoc(b, :activity)) - |> join(:inner, [_b, a], bookmarks in assoc(a, :bookmarks)) - |> preload([b, a, b2], activity: {a, bookmarks: b2}) + |> preload([b, a], activity: a) end def get(user_id, activity_id) do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index bd2544470..a4053986f 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -137,13 +137,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do activity end - activity = - if activity.data["type"] in ["Create", "Announce"] do - Repo.preload(activity, :bookmarks) - else - activity - end - Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end) @@ -822,11 +815,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Activity.with_preloaded_object() end + defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query + + defp maybe_preload_bookmarks(query, opts) do + query + |> Activity.with_preloaded_bookmark(opts["user"]) + end + def fetch_activities_query(recipients, opts \\ %{}) do base_query = from(activity in Activity) base_query |> maybe_preload_objects(opts) + |> maybe_preload_bookmarks(opts) |> restrict_recipients(recipients, opts["user"]) |> restrict_tag(opts) |> restrict_tag_reject(opts) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 451fc85ce..83ad90989 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -563,9 +563,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do with %Activity{} = activity <- Activity.get_by_id_with_object(id), %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), - {:ok, bookmark} <- Bookmark.create(user.id, activity.id) do - activity = %{activity | bookmarks: [bookmark | activity.bookmarks]} - + {:ok, _bookmark} <- Bookmark.create(user.id, activity.id) do conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) @@ -577,11 +575,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do %User{} = user <- User.get_cached_by_nickname(user.nickname), true <- Visibility.visible_for_user?(activity, user), {:ok, _bookmark} <- Bookmark.destroy(user.id, activity.id) do - bookmarks = - Enum.filter(activity.bookmarks, fn %{user_id: user_id} -> user_id != user.id end) - - activity = %{activity | bookmarks: bookmarks} - conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) @@ -1154,7 +1147,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do activities = bookmarks - |> Enum.map(fn b -> b.activity end) + |> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end) conn |> add_link_headers(:bookmarks, bookmarks) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index c5a7bcbab..bd2372944 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -75,26 +75,22 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do def render( "status.json", - %{activity: %{data: %{"type" => "Announce", "object" => object}} = activity} = opts + %{activity: %{data: %{"type" => "Announce", "object" => _object}} = activity} = opts ) do user = get_user(activity.data["actor"]) created_at = Utils.to_masto_date(activity.data["published"]) + activity_object = Object.normalize(activity) reblogged_activity = - Activity.create_by_object_ap_id(object) - |> Activity.with_preloaded_bookmarks() + Activity.create_by_object_ap_id(activity_object.data["id"]) + |> Activity.with_preloaded_bookmark(opts[:for]) |> Repo.one() reblogged = render("status.json", Map.put(opts, :activity, reblogged_activity)) - activity_object = Object.normalize(activity) favorited = opts[:for] && opts[:for].ap_id in (activity_object.data["likes"] || []) - bookmarked = - opts[:for] && Ecto.assoc_loaded?(reblogged_activity.bookmarks) && - Enum.any?(reblogged_activity.bookmarks, fn %{user_id: user_id} -> - user_id == opts[:for].id - end) + bookmarked = Activity.get_bookmark(reblogged_activity, opts[:for]) != nil mentions = activity.recipients @@ -104,8 +100,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do %{ id: to_string(activity.id), - uri: object, - url: object, + uri: activity_object.data["id"], + url: activity_object.data["id"], account: AccountView.render("account.json", %{user: user}), in_reply_to_id: nil, in_reply_to_account_id: nil, @@ -157,11 +153,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do favorited = opts[:for] && opts[:for].ap_id in (object.data["likes"] || []) - bookmarked = - opts[:for] && Ecto.assoc_loaded?(activity.bookmarks) && - Enum.any?(activity.bookmarks, fn %{user_id: user_id} -> - user_id == opts[:for].id - end) + bookmarked = Activity.get_bookmark(activity, opts[:for]) != nil attachment_data = object.data["attachment"] || [] attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment) -- cgit v1.2.3 From 6020ff3fb696c6b46e948d69a8687182c701a743 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Tue, 7 May 2019 19:30:27 +0000 Subject: activitypub: add optional order constraint to timeline query builder --- lib/pleroma/web/activity_pub/activity_pub.ex | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index a4053986f..8f8c23a9b 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -822,12 +822,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Activity.with_preloaded_bookmark(opts["user"]) end + defp maybe_order(query, %{order: :desc}) do + query + |> order_by(desc: :id) + end + + defp maybe_order(query, %{order: :asc}) do + query + |> order_by(asc: :id) + end + + defp maybe_order(query, _), do: query + def fetch_activities_query(recipients, opts \\ %{}) do base_query = from(activity in Activity) base_query |> maybe_preload_objects(opts) |> maybe_preload_bookmarks(opts) + |> maybe_order(opts) |> restrict_recipients(recipients, opts["user"]) |> restrict_tag(opts) |> restrict_tag_reject(opts) -- cgit v1.2.3 From d64c3b604e4eb5e8dd3f51aff7d1f15024d5fa33 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Tue, 7 May 2019 19:30:51 +0000 Subject: twitterapi: use order constraint to force descending order --- lib/pleroma/web/twitter_api/twitter_api_controller.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index ef7b6fe65..21e6c555a 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -182,6 +182,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do |> Map.put("blocking_user", user) |> Map.put("user", user) |> Map.put(:visibility, "direct") + |> Map.put(:order, :desc) activities = ActivityPub.fetch_activities_query([user.ap_id], params) -- cgit v1.2.3 From 799e1f48b52762f21f577269938bc037eba9e20f Mon Sep 17 00:00:00 2001 From: Maksim Date: Wed, 8 May 2019 10:52:13 +0000 Subject: Refactoring functions for dealing with oauth scopes. --- lib/pleroma/web/controller_helper.ex | 6 -- .../web/mastodon_api/mastodon_api_controller.ex | 3 +- lib/pleroma/web/oauth.ex | 14 ----- lib/pleroma/web/oauth/oauth_controller.ex | 31 +++++----- lib/pleroma/web/oauth/scopes.ex | 67 ++++++++++++++++++++++ 5 files changed, 86 insertions(+), 35 deletions(-) create mode 100644 lib/pleroma/web/oauth/scopes.ex (limited to 'lib') diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index 181483664..55706eeb8 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -10,12 +10,6 @@ defmodule Pleroma.Web.ControllerHelper do def truthy_param?(blank_value) when blank_value in [nil, ""], do: nil def truthy_param?(value), do: value not in @falsy_param_values - def oauth_scopes(params, default) do - # Note: `scopes` is used by Mastodon — supporting it but sticking to - # OAuth's standard `scope` wherever we control it - Pleroma.Web.OAuth.parse_scopes(params["scope"] || params["scopes"], default) - end - def json_response(conn, status, json) do conn |> put_status(status) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 83ad90989..956736780 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -37,6 +37,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.Web.MediaProxy alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Authorization + alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token alias Pleroma.Web.ControllerHelper @@ -50,7 +51,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do action_fallback(:errors) def create_app(conn, params) do - scopes = ControllerHelper.oauth_scopes(params, ["read"]) + scopes = Scopes.fetch_scopes(params, ["read"]) app_attrs = params diff --git a/lib/pleroma/web/oauth.ex b/lib/pleroma/web/oauth.ex index d2835a0ba..280cf28c0 100644 --- a/lib/pleroma/web/oauth.ex +++ b/lib/pleroma/web/oauth.ex @@ -3,18 +3,4 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OAuth do - def parse_scopes(scopes, _default) when is_list(scopes) do - Enum.filter(scopes, &(&1 not in [nil, ""])) - end - - def parse_scopes(scopes, default) when is_binary(scopes) do - scopes - |> String.trim() - |> String.split(~r/[\s,]+/) - |> parse_scopes(default) - end - - def parse_scopes(_, default) do - default - end end diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index e3c01217d..8ee0da667 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -15,8 +15,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do alias Pleroma.Web.OAuth.Token alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken - - import Pleroma.Web.ControllerHelper, only: [oauth_scopes: 2] + alias Pleroma.Web.OAuth.Scopes if Pleroma.Config.oauth_consumer_enabled?(), do: plug(Ueberauth) @@ -57,7 +56,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do defp do_authorize(conn, params) do app = Repo.get_by(App, client_id: params["client_id"]) available_scopes = (app && app.scopes) || [] - scopes = oauth_scopes(params, nil) || available_scopes + scopes = Scopes.fetch_scopes(params, available_scopes) # Note: `params` might differ from `conn.params`; use `@params` not `@conn.params` in template render(conn, Authenticator.auth_template(), %{ @@ -113,7 +112,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do defp handle_create_authorization_error( conn, - {scopes_issue, _}, + {:error, scopes_issue}, %{"authorization" => _} = params ) when scopes_issue in [:unsupported_scopes, :missing_scopes] do @@ -184,9 +183,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do %App{} = app <- get_app_from_request(conn, params), {:auth_active, true} <- {:auth_active, User.auth_active?(user)}, {:user_active, true} <- {:user_active, !user.info.deactivated}, - scopes <- oauth_scopes(params, app.scopes), - [] <- scopes -- app.scopes, - true <- Enum.any?(scopes), + {:ok, scopes} <- validate_scopes(app, params), {:ok, auth} <- Authorization.create_authorization(app, user, scopes), {:ok, token} <- Token.exchange_token(app, auth) do json(conn, response_token(user, token)) @@ -247,8 +244,9 @@ defmodule Pleroma.Web.OAuth.OAuthController do @doc "Prepares OAuth request to provider for Ueberauth" def prepare_request(conn, %{"provider" => provider, "authorization" => auth_attrs}) do scope = - oauth_scopes(auth_attrs, []) - |> Enum.join(" ") + auth_attrs + |> Scopes.fetch_scopes([]) + |> Scopes.to_string() state = auth_attrs @@ -326,7 +324,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do client_id: auth_attrs["client_id"], redirect_uri: auth_attrs["redirect_uri"], state: auth_attrs["state"], - scopes: oauth_scopes(auth_attrs, []), + scopes: Scopes.fetch_scopes(auth_attrs, []), nickname: auth_attrs["nickname"], email: auth_attrs["email"] }) @@ -401,10 +399,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do {: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), - scopes <- oauth_scopes(auth_attrs, []), - {:unsupported_scopes, []} <- {:unsupported_scopes, scopes -- app.scopes}, - # Note: `scope` param is intentionally not optional in this context - {:missing_scopes, false} <- {:missing_scopes, scopes == []}, + {:ok, scopes} <- validate_scopes(app, auth_attrs), {:auth_active, true} <- {:auth_active, User.auth_active?(user)} do Authorization.create_authorization(app, user, scopes) end @@ -458,4 +453,12 @@ defmodule Pleroma.Web.OAuth.OAuthController do } |> Map.merge(opts) end + + @spec validate_scopes(App.t(), map()) :: + {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes} + defp validate_scopes(app, params) do + params + |> Scopes.fetch_scopes(app.scopes) + |> Scopes.validates(app.scopes) + end end diff --git a/lib/pleroma/web/oauth/scopes.ex b/lib/pleroma/web/oauth/scopes.ex new file mode 100644 index 000000000..ad9dfb260 --- /dev/null +++ b/lib/pleroma/web/oauth/scopes.ex @@ -0,0 +1,67 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OAuth.Scopes do + @moduledoc """ + Functions for dealing with scopes. + """ + + @doc """ + Fetch scopes from requiest params. + + Note: `scopes` is used by Mastodon — supporting it but sticking to + OAuth's standard `scope` wherever we control it + """ + @spec fetch_scopes(map(), list()) :: list() + def fetch_scopes(params, default) do + parse_scopes(params["scope"] || params["scopes"], default) + end + + def parse_scopes(scopes, _default) when is_list(scopes) do + Enum.filter(scopes, &(&1 not in [nil, ""])) + end + + def parse_scopes(scopes, default) when is_binary(scopes) do + scopes + |> to_list + |> parse_scopes(default) + end + + def parse_scopes(_, default) do + default + end + + @doc """ + Convert scopes string to list + """ + @spec to_list(binary()) :: [binary()] + def to_list(nil), do: [] + + def to_list(str) do + str + |> String.trim() + |> String.split(~r/[\s,]+/) + end + + @doc """ + Convert scopes list to string + """ + @spec to_string(list()) :: binary() + def to_string(scopes), do: Enum.join(scopes, " ") + + @doc """ + Validates scopes. + """ + @spec validates(list() | nil, list()) :: + {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes} + def validates([], _app_scopes), do: {:error, :missing_scopes} + def validates(nil, _app_scopes), do: {:error, :missing_scopes} + + def validates(scopes, app_scopes) do + case scopes -- app_scopes do + [] -> {:ok, scopes} + _ -> {:error, :unsupported_scopes} + end + end +end -- cgit v1.2.3 From a76e2622ca0201dfd78616cbad58c9fb14111796 Mon Sep 17 00:00:00 2001 From: Alex S Date: Wed, 8 May 2019 19:05:25 +0700 Subject: emoji dowload follow redirects --- lib/mix/tasks/pleroma/emoji.ex | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/emoji.ex b/lib/mix/tasks/pleroma/emoji.ex index cced73226..5cb54c3ca 100644 --- a/lib/mix/tasks/pleroma/emoji.ex +++ b/lib/mix/tasks/pleroma/emoji.ex @@ -109,7 +109,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do ]) ) - binary_archive = Tesla.get!(src_url).body + binary_archive = Tesla.get!(client(), src_url).body archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16() sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright] @@ -137,7 +137,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do ]) ) - files = Tesla.get!(files_url).body |> Poison.decode!() + files = Tesla.get!(client(), files_url).body |> Poison.decode!() IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name])) @@ -213,7 +213,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do IO.puts("Downloading the pack and generating SHA256") - binary_archive = Tesla.get!(src).body + binary_archive = Tesla.get!(client(), src).body archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16() IO.puts("SHA256 is #{archive_sha}") @@ -272,7 +272,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do defp fetch_manifest(from) do Poison.decode!( if String.starts_with?(from, "http") do - Tesla.get!(from).body + Tesla.get!(client(), from).body else File.read!(from) end @@ -290,4 +290,12 @@ defmodule Mix.Tasks.Pleroma.Emoji do ] ) end + + defp client do + middleware = [ + {Tesla.Middleware.FollowRedirects, [max_redirects: 3]} + ] + + Tesla.client(middleware) + end end -- cgit v1.2.3 From bfeb33e951c8997f33a0242fe69b8e669e3afe07 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Wed, 8 May 2019 14:34:36 +0000 Subject: Merge develop Merge conflict in lib/pleroma/activity.ex --- lib/pleroma/activity.ex | 23 +++ lib/pleroma/stats.ex | 9 +- lib/pleroma/user.ex | 196 +++++----------------- lib/pleroma/user/query.ex | 150 +++++++++++++++++ lib/pleroma/web/admin_api/admin_api_controller.ex | 9 +- lib/pleroma/web/admin_api/search.ex | 44 ++--- 6 files changed, 236 insertions(+), 195 deletions(-) create mode 100644 lib/pleroma/user/query.ex (limited to 'lib') diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 2b661edc1..c121e800f 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -287,6 +287,29 @@ defmodule Pleroma.Activity do |> Repo.all() end + def follow_requests_for_actor(%Pleroma.User{ap_id: ap_id}) do + from( + a in Activity, + where: + fragment( + "? ->> 'type' = 'Follow'", + a.data + ), + where: + fragment( + "? ->> 'state' = 'pending'", + a.data + ), + where: + fragment( + "coalesce((?)->'object'->>'id', (?)->>'object') = ?", + a.data, + a.data, + ^ap_id + ) + ) + end + @spec query_by_actor(actor()) :: Ecto.Query.t() def query_by_actor(actor) do from(a in Activity, where: a.actor == ^actor) diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex index 2e7d747df..5b242927b 100644 --- a/lib/pleroma/stats.ex +++ b/lib/pleroma/stats.ex @@ -34,7 +34,7 @@ defmodule Pleroma.Stats do def update_stats do peers = from( - u in Pleroma.User, + u in User, select: fragment("distinct split_part(?, '@', 2)", u.nickname), where: u.local != ^true ) @@ -44,10 +44,13 @@ defmodule Pleroma.Stats do domain_count = Enum.count(peers) status_query = - from(u in User.local_user_query(), select: fragment("sum((?->>'note_count')::int)", u.info)) + from(u in User.Query.build(%{local: true}), + select: fragment("sum((?->>'note_count')::int)", u.info) + ) status_count = Repo.one(status_query) - user_count = Repo.aggregate(User.active_local_user_query(), :count, :id) + + user_count = Repo.aggregate(User.Query.build(%{local: true, active: true}), :count, :id) Agent.update(__MODULE__, fn _ -> {peers, %{domain_count: domain_count, status_count: status_count, user_count: user_count}} diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index b1adaad2f..427400aa1 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -254,10 +254,7 @@ defmodule Pleroma.User do candidates = Pleroma.Config.get([:instance, :autofollowed_nicknames]) autofollowed_users = - from(u in User, - where: u.local == true, - where: u.nickname in ^candidates - ) + User.Query.build(%{nickname: candidates, local: true}) |> Repo.all() follow_all(user, autofollowed_users) @@ -576,19 +573,17 @@ defmodule Pleroma.User do ) end - def get_followers_query(%User{id: id, follower_address: follower_address}, nil) do - from( - u in User, - where: fragment("? <@ ?", ^[follower_address], u.following), - where: u.id != ^id - ) + @spec get_followers_query(User.t(), pos_integer() | nil) :: Ecto.Query.t() + def get_followers_query(%User{} = user, nil) do + User.Query.build(%{followers: user}) end def get_followers_query(user, page) do from(u in get_followers_query(user, nil)) - |> paginate(page, 20) + |> User.Query.paginate(page, 20) end + @spec get_followers_query(User.t()) :: Ecto.Query.t() def get_followers_query(user), do: get_followers_query(user, nil) def get_followers(user, page \\ nil) do @@ -603,19 +598,17 @@ defmodule Pleroma.User do Repo.all(from(u in q, select: u.id)) end - def get_friends_query(%User{id: id, following: following}, nil) do - from( - u in User, - where: u.follower_address in ^following, - where: u.id != ^id - ) + @spec get_friends_query(User.t(), pos_integer() | nil) :: Ecto.Query.t() + def get_friends_query(%User{} = user, nil) do + User.Query.build(%{friends: user}) end def get_friends_query(user, page) do from(u in get_friends_query(user, nil)) - |> paginate(page, 20) + |> User.Query.paginate(page, 20) end + @spec get_friends_query(User.t()) :: Ecto.Query.t() def get_friends_query(user), do: get_friends_query(user, nil) def get_friends(user, page \\ nil) do @@ -630,33 +623,10 @@ defmodule Pleroma.User do Repo.all(from(u in q, select: u.id)) end - def get_follow_requests_query(%User{} = user) do - from( - a in Activity, - where: - fragment( - "? ->> 'type' = 'Follow'", - a.data - ), - where: - fragment( - "? ->> 'state' = 'pending'", - a.data - ), - where: - fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ?", - a.data, - a.data, - ^user.ap_id - ) - ) - end - + @spec get_follow_requests(User.t()) :: {:ok, [User.t()]} def get_follow_requests(%User{} = user) do users = - user - |> User.get_follow_requests_query() + Activity.follow_requests_for_actor(user) |> join(:inner, [a], u in User, on: a.actor == u.ap_id) |> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address])) |> group_by([a, u], u.id) @@ -729,10 +699,7 @@ defmodule Pleroma.User do def update_follower_count(%User{} = user) do follower_count_query = - User - |> where([u], ^user.follower_address in u.following) - |> where([u], u.id != ^user.id) - |> select([u], %{count: count(u.id)}) + User.Query.build(%{followers: user}) |> select([u], %{count: count(u.id)}) User |> where(id: ^user.id) @@ -755,38 +722,19 @@ defmodule Pleroma.User do end end - def get_users_from_set_query(ap_ids, false) do - from( - u in User, - where: u.ap_id in ^ap_ids - ) - end - - def get_users_from_set_query(ap_ids, true) do - query = get_users_from_set_query(ap_ids, false) - - from( - u in query, - where: u.local == true - ) - end - + @spec get_users_from_set([String.t()], boolean()) :: [User.t()] def get_users_from_set(ap_ids, local_only \\ true) do - get_users_from_set_query(ap_ids, local_only) + criteria = %{ap_id: ap_ids} + criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria + + User.Query.build(criteria) |> Repo.all() end + @spec get_recipients_from_activity(Activity.t()) :: [User.t()] def get_recipients_from_activity(%Activity{recipients: to}) do - query = - from( - u in User, - where: u.ap_id in ^to, - or_where: fragment("? && ?", u.following, ^to) - ) - - query = from(u in query, where: u.local == true) - - Repo.all(query) + User.Query.build(%{recipients_from_activity: to, local: true}) + |> Repo.all() end def search(query, resolve \\ false, for_user \\ nil) do @@ -1048,14 +996,23 @@ defmodule Pleroma.User do end end - def muted_users(user), - do: Repo.all(from(u in User, where: u.ap_id in ^user.info.mutes)) + @spec muted_users(User.t()) :: [User.t()] + def muted_users(user) do + User.Query.build(%{ap_id: user.info.mutes}) + |> Repo.all() + end - def blocked_users(user), - do: Repo.all(from(u in User, where: u.ap_id in ^user.info.blocks)) + @spec blocked_users(User.t()) :: [User.t()] + def blocked_users(user) do + User.Query.build(%{ap_id: user.info.blocks}) + |> Repo.all() + end - def subscribers(user), - do: Repo.all(from(u in User, where: u.ap_id in ^user.info.subscribers)) + @spec subscribers(User.t()) :: [User.t()] + def subscribers(user) do + User.Query.build(%{ap_id: user.info.subscribers}) + |> Repo.all() + end def block_domain(user, domain) do info_cng = @@ -1081,69 +1038,6 @@ defmodule Pleroma.User do update_and_set_cache(cng) end - def maybe_local_user_query(query, local) do - if local, do: local_user_query(query), else: query - end - - def local_user_query(query \\ User) do - from( - u in query, - where: u.local == true, - where: not is_nil(u.nickname) - ) - end - - def maybe_external_user_query(query, external) do - if external, do: external_user_query(query), else: query - end - - def external_user_query(query \\ User) do - from( - u in query, - where: u.local == false, - where: not is_nil(u.nickname) - ) - end - - def maybe_active_user_query(query, active) do - if active, do: active_user_query(query), else: query - end - - def active_user_query(query \\ User) do - from( - u in query, - where: fragment("not (?->'deactivated' @> 'true')", u.info), - where: not is_nil(u.nickname) - ) - end - - def maybe_deactivated_user_query(query, deactivated) do - if deactivated, do: deactivated_user_query(query), else: query - end - - def deactivated_user_query(query \\ User) do - from( - u in query, - where: fragment("(?->'deactivated' @> 'true')", u.info), - where: not is_nil(u.nickname) - ) - end - - def active_local_user_query do - from( - u in local_user_query(), - where: fragment("not (?->'deactivated' @> 'true')", u.info) - ) - end - - def moderator_user_query do - from( - u in User, - where: u.local == true, - where: fragment("?->'is_moderator' @> 'true'", u.info) - ) - end - def deactivate(%User{} = user, status \\ true) do info_cng = User.Info.set_activation_status(user.info, status) @@ -1306,7 +1200,7 @@ defmodule Pleroma.User do def ap_enabled?(_), do: false @doc "Gets or fetch a user by uri or nickname." - @spec get_or_fetch(String.t()) :: User.t() + @spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()} def get_or_fetch("http" <> _host = uri), do: get_or_fetch_by_ap_id(uri) def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname) @@ -1423,22 +1317,12 @@ defmodule Pleroma.User do } end + @spec all_superusers() :: [User.t()] def all_superusers do - from( - u in User, - where: u.local == true, - where: fragment("?->'is_admin' @> 'true' OR ?->'is_moderator' @> 'true'", u.info, u.info) - ) + User.Query.build(%{super_users: true, local: true}) |> Repo.all() end - defp paginate(query, page, page_size) do - from(u in query, - limit: ^page_size, - offset: ^((page - 1) * page_size) - ) - end - def showing_reblogs?(%User{} = user, %User{} = target) do target.ap_id not in user.info.muted_reblogs end diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex new file mode 100644 index 000000000..2dfe5ce92 --- /dev/null +++ b/lib/pleroma/user/query.ex @@ -0,0 +1,150 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.Query do + @moduledoc """ + User query builder module. Builds query from new query or another user query. + + ## Example: + query = Pleroma.User.Query(%{nickname: "nickname"}) + another_query = Pleroma.User.Query.build(query, %{email: "email@example.com"}) + Pleroma.Repo.all(query) + Pleroma.Repo.all(another_query) + + Adding new rules: + - *ilike criteria* + - add field to @ilike_criteria list + - pass non empty string + - e.g. Pleroma.User.Query.build(%{nickname: "nickname"}) + - *equal criteria* + - add field to @equal_criteria list + - pass non empty string + - e.g. Pleroma.User.Query.build(%{email: "email@example.com"}) + - *contains criteria* + - add field to @containns_criteria list + - pass values list + - e.g. Pleroma.User.Query.build(%{ap_id: ["http://ap_id1", "http://ap_id2"]}) + """ + import Ecto.Query + import Pleroma.Web.AdminAPI.Search, only: [not_empty_string: 1] + alias Pleroma.User + + @type criteria :: + %{ + query: String.t(), + tags: [String.t()], + name: String.t(), + email: String.t(), + local: boolean(), + external: boolean(), + active: boolean(), + deactivated: boolean(), + is_admin: boolean(), + is_moderator: boolean(), + super_users: boolean(), + followers: User.t(), + friends: User.t(), + recipients_from_activity: [String.t()], + nickname: [String.t()], + ap_id: [String.t()] + } + | %{} + + @ilike_criteria [:nickname, :name, :query] + @equal_criteria [:email] + @role_criteria [:is_admin, :is_moderator] + @contains_criteria [:ap_id, :nickname] + + @spec build(criteria()) :: Query.t() + def build(query \\ base_query(), criteria) do + prepare_query(query, criteria) + end + + @spec paginate(Ecto.Query.t(), pos_integer(), pos_integer()) :: Ecto.Query.t() + def paginate(query, page, page_size) do + from(u in query, + limit: ^page_size, + offset: ^((page - 1) * page_size) + ) + end + + defp base_query do + from(u in User) + end + + defp prepare_query(query, criteria) do + Enum.reduce(criteria, query, &compose_query/2) + end + + defp compose_query({key, value}, query) + when key in @ilike_criteria and not_empty_string(value) do + # hack for :query key + key = if key == :query, do: :nickname, else: key + where(query, [u], ilike(field(u, ^key), ^"%#{value}%")) + end + + defp compose_query({key, value}, query) + when key in @equal_criteria and not_empty_string(value) do + where(query, [u], ^[{key, value}]) + end + + defp compose_query({key, values}, query) when key in @contains_criteria and is_list(values) do + where(query, [u], field(u, ^key) in ^values) + end + + defp compose_query({:tags, tags}, query) when is_list(tags) and length(tags) > 0 do + Enum.reduce(tags, query, &prepare_tag_criteria/2) + end + + defp compose_query({key, _}, query) when key in @role_criteria do + where(query, [u], fragment("(?->? @> 'true')", u.info, ^to_string(key))) + end + + defp compose_query({:super_users, _}, query) do + where( + query, + [u], + fragment("?->'is_admin' @> 'true' OR ?->'is_moderator' @> 'true'", u.info, u.info) + ) + end + + defp compose_query({:local, _}, query), do: location_query(query, true) + + defp compose_query({:external, _}, query), do: location_query(query, false) + + defp compose_query({:active, _}, query) do + where(query, [u], fragment("not (?->'deactivated' @> 'true')", u.info)) + |> where([u], not is_nil(u.nickname)) + end + + defp compose_query({:deactivated, _}, query) do + where(query, [u], fragment("?->'deactivated' @> 'true'", u.info)) + |> where([u], not is_nil(u.nickname)) + end + + defp compose_query({:followers, %User{id: id, follower_address: follower_address}}, query) do + where(query, [u], fragment("? <@ ?", ^[follower_address], u.following)) + |> where([u], u.id != ^id) + end + + defp compose_query({:friends, %User{id: id, following: following}}, query) do + where(query, [u], u.follower_address in ^following) + |> where([u], u.id != ^id) + end + + defp compose_query({:recipients_from_activity, to}, query) do + where(query, [u], u.ap_id in ^to or fragment("? && ?", u.following, ^to)) + end + + defp compose_query(_unsupported_param, query), do: query + + defp prepare_tag_criteria(tag, query) do + or_where(query, [u], fragment("? = any(?)", ^tag, u.tags)) + end + + defp location_query(query, local) do + where(query, [u], u.local == ^local) + |> where([u], not is_nil(u.nickname)) + 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 711f233a6..b553d96a8 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -101,7 +101,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do search_params = %{ query: params["query"], page: page, - page_size: page_size + page_size: page_size, + tags: params["tags"], + name: params["name"], + email: params["email"] } with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)), @@ -116,11 +119,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do ) end - @filters ~w(local external active deactivated) + @filters ~w(local external active deactivated is_admin is_moderator) + @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{} defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{} - @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{} defp maybe_parse_filters(filters) do filters |> String.split(",") diff --git a/lib/pleroma/web/admin_api/search.ex b/lib/pleroma/web/admin_api/search.ex index 9a8e41c2a..ed919833e 100644 --- a/lib/pleroma/web/admin_api/search.ex +++ b/lib/pleroma/web/admin_api/search.ex @@ -10,45 +10,23 @@ defmodule Pleroma.Web.AdminAPI.Search do @page_size 50 - def user(%{query: term} = params) when is_nil(term) or term == "" do - query = maybe_filtered_query(params) + defmacro not_empty_string(string) do + quote do + is_binary(unquote(string)) and unquote(string) != "" + end + end + + @spec user(map()) :: {:ok, [User.t()], pos_integer()} + def user(params \\ %{}) do + query = User.Query.build(params) |> order_by([u], u.nickname) paginated_query = - maybe_filtered_query(params) - |> paginate(params[:page] || 1, params[:page_size] || @page_size) + User.Query.paginate(query, params[:page] || 1, params[:page_size] || @page_size) - count = query |> Repo.aggregate(:count, :id) + count = Repo.aggregate(query, :count, :id) results = Repo.all(paginated_query) {:ok, results, count} end - - def user(%{query: term} = params) when is_binary(term) do - search_query = from(u in maybe_filtered_query(params), where: ilike(u.nickname, ^"%#{term}%")) - - count = search_query |> Repo.aggregate(:count, :id) - - results = - search_query - |> paginate(params[:page] || 1, params[:page_size] || @page_size) - |> Repo.all() - - {:ok, results, count} - end - - defp maybe_filtered_query(params) do - from(u in User, order_by: u.nickname) - |> User.maybe_local_user_query(params[:local]) - |> User.maybe_external_user_query(params[:external]) - |> User.maybe_active_user_query(params[:active]) - |> User.maybe_deactivated_user_query(params[:deactivated]) - end - - defp paginate(query, page, page_size) do - from(u in query, - limit: ^page_size, - offset: ^((page - 1) * page_size) - ) - end end -- cgit v1.2.3 From e4523c301023de0d4cff5ca0168094fea25955a2 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 9 May 2019 22:27:00 +0300 Subject: Fix get_in_reply_to in OStatus' activity representer depending on embedded objects --- lib/pleroma/web/ostatus/activity_representer.ex | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex index 166691a09..a7832a3d9 100644 --- a/lib/pleroma/web/ostatus/activity_representer.ex +++ b/lib/pleroma/web/ostatus/activity_representer.ex @@ -18,15 +18,18 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do end end - defp get_in_reply_to(%{"object" => %{"inReplyTo" => in_reply_to}}) do - [ - {:"thr:in-reply-to", - [ref: to_charlist(in_reply_to), href: to_charlist(get_href(in_reply_to))], []} - ] + defp get_in_reply_to(activity) do + with %Object{data: %{"inReplyTo" => in_reply_to}} <- Object.normalize(activity) do + [ + {:"thr:in-reply-to", + [ref: to_charlist(in_reply_to), href: to_charlist(get_href(in_reply_to))], []} + ] + else + _ -> + [] + end end - defp get_in_reply_to(_), do: [] - defp get_mentions(to) do Enum.map(to, fn id -> cond do @@ -98,7 +101,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do []} end) - in_reply_to = get_in_reply_to(activity.data) + in_reply_to = get_in_reply_to(activity) author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] mentions = activity.recipients |> get_mentions -- cgit v1.2.3 From 2a54e8c1f51cb681cefc5ba08ad0f1c7ed1fdc6d Mon Sep 17 00:00:00 2001 From: Francis Dinh Date: Thu, 9 May 2019 19:48:41 -0400 Subject: Use "repeated" instead of "retweeted" for repeated statuses in Twitter API This makes it consistent with the language used in Pleroma FE (at least in English). Resolves https://git.pleroma.social/pleroma/pleroma-fe/issues/533 --- lib/pleroma/web/twitter_api/views/activity_view.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index c64152da8..d084ad734 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -170,7 +170,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do created_at = activity.data["published"] |> Utils.date_to_asctime() announced_activity = Activity.get_create_by_object_ap_id(activity.data["object"]) - text = "#{user.nickname} retweeted a status." + text = "#{user.nickname} repeated a status." retweeted_status = render("activity.json", Map.merge(opts, %{activity: announced_activity})) -- cgit v1.2.3 From 1d78e42fd4ca73402c4101ac01b9abb44f4f8cf6 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 10 May 2019 13:49:34 +0300 Subject: Remove get_in_reply_to calls in some functions because the result is unused and it does not have any side-effects --- lib/pleroma/web/ostatus/activity_representer.ex | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex index a7832a3d9..95037125d 100644 --- a/lib/pleroma/web/ostatus/activity_representer.ex +++ b/lib/pleroma/web/ostatus/activity_representer.ex @@ -149,7 +149,6 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do updated_at = activity.data["published"] inserted_at = activity.data["published"] - _in_reply_to = get_in_reply_to(activity.data) author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] mentions = activity.recipients |> get_mentions @@ -180,7 +179,6 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do updated_at = activity.data["published"] inserted_at = activity.data["published"] - _in_reply_to = get_in_reply_to(activity.data) author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] retweeted_activity = Activity.get_create_by_object_ap_id(activity.data["object"]) -- cgit v1.2.3 From c953ae8c3d75beb55ba02b3bada864d019470b05 Mon Sep 17 00:00:00 2001 From: feld Date: Sat, 11 May 2019 01:34:17 +0000 Subject: Initial bundle of basic AdminFE Due to CSP headers we only allow connecting to self. If you want to host AdminFE on a separate domain without CSP headers you will be able to connect to any public Pleroma host. --- lib/pleroma/web/endpoint.ex | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib') diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index 7f939991d..9ef30e885 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -29,6 +29,13 @@ defmodule Pleroma.Web.Endpoint do # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength ) + plug(Plug.Static.IndexHtml, at: "/pleroma/admin/") + + plug(Plug.Static, + at: "/pleroma/admin/", + from: {:pleroma, "priv/static/adminfe/"} + ) + # Code reloading can be explicitly enabled under the # :code_reloader configuration of your endpoint. if code_reloading? do -- cgit v1.2.3 From f01f995816d86999d5cbf261605887b3a3b313ac Mon Sep 17 00:00:00 2001 From: Sachin Joshi Date: Sat, 11 May 2019 14:17:04 +0545 Subject: make url uniform in admin api --- lib/pleroma/web/router.ex | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 6d9c77c1a..8b84fbbad 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -146,34 +146,52 @@ defmodule Pleroma.Web.Router do scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do pipe_through([:admin_api, :oauth_write]) - post("/user/follow", AdminAPIController, :user_follow) - post("/user/unfollow", AdminAPIController, :user_unfollow) - - get("/users", AdminAPIController, :list_users) - get("/users/:nickname", AdminAPIController, :user_show) + post("/users/follow", AdminAPIController, :user_follow) + post("/users/unfollow", AdminAPIController, :user_unfollow) + # TODO: to be removed at version 1.0 delete("/user", AdminAPIController, :user_delete) - patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation) post("/user", AdminAPIController, :user_create) + + delete("/users", AdminAPIController, :user_delete) + post("/users", AdminAPIController, :user_create) + patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation) put("/users/tag", AdminAPIController, :tag_users) delete("/users/tag", AdminAPIController, :untag_users) + # TODO: to be removed at version 1.0 get("/permission_group/:nickname", AdminAPIController, :right_get) get("/permission_group/:nickname/:permission_group", AdminAPIController, :right_get) post("/permission_group/:nickname/:permission_group", AdminAPIController, :right_add) delete("/permission_group/:nickname/:permission_group", AdminAPIController, :right_delete) - put("/activation_status/:nickname", AdminAPIController, :set_activation_status) + get("/users/:nickname/permission_group", AdminAPIController, :right_get) + get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get) + post("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_add) + + delete( + "/users/:nickname/permission_group/:permission_group", + AdminAPIController, + :right_delete + ) + + put("/users/:nickname/activation_status", AdminAPIController, :set_activation_status) post("/relay", AdminAPIController, :relay_follow) delete("/relay", AdminAPIController, :relay_unfollow) - get("/invite_token", AdminAPIController, :get_invite_token) - get("/invites", AdminAPIController, :invites) - post("/revoke_invite", AdminAPIController, :revoke_invite) - post("/email_invite", AdminAPIController, :email_invite) + get("/users/invite_token", AdminAPIController, :get_invite_token) + get("/users/invites", AdminAPIController, :invites) + post("/users/revoke_invite", AdminAPIController, :revoke_invite) + post("/users/email_invite", AdminAPIController, :email_invite) + # TODO: to be removed at version 1.0 get("/password_reset", AdminAPIController, :get_password_reset) + + get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset) + + get("/users", AdminAPIController, :list_users) + get("/users/:nickname", AdminAPIController, :user_show) end scope "/", Pleroma.Web.TwitterAPI do -- cgit v1.2.3 From b9f84a382a0e4bbee09e21346f9293962b80917c Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sun, 12 May 2019 03:01:42 +0300 Subject: Normalize the object only after ensuring the activity type is Create --- lib/pleroma/conversation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex index 6e26c5fd4..0db195988 100644 --- a/lib/pleroma/conversation.ex +++ b/lib/pleroma/conversation.ex @@ -47,8 +47,8 @@ defmodule Pleroma.Conversation do """ def create_or_bump_for(activity) do with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), - object <- Pleroma.Object.normalize(activity), "Create" <- activity.data["type"], + object <- Pleroma.Object.normalize(activity), "Note" <- object.data["type"], ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do {:ok, conversation} = create_for_ap_id(ap_id) -- cgit v1.2.3 From e7d292f80ee03d6eabf30792640a7a40e041a796 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 02:41:34 +0000 Subject: federator: add publisher module defining a contract for publishing behaviours --- lib/pleroma/web/federator/publisher.ex | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 lib/pleroma/web/federator/publisher.ex (limited to 'lib') diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex new file mode 100644 index 000000000..36277fd7e --- /dev/null +++ b/lib/pleroma/web/federator/publisher.ex @@ -0,0 +1,38 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Federator.Publisher do + @moduledoc """ + Defines the contract used by federation implementations to publish messages to + their peers. + """ + + @doc """ + Determine whether an activity can be relayed using the federation module. + """ + @callback is_representable?(Pleroma.Activity.t()) :: boolean() + + @doc """ + Relays an activity to a specified peer, determined by the parameters. The + parameters used are controlled by the federation module. + """ + @callback publish_one(Map.t()) :: {:ok, Map.t()} | {:error, any()} + + @doc """ + Relays an activity to all specified peers. + """ + @callback publish(Pleroma.User.t(), Pleroma.Activity.t()) :: :ok | {:error, any()} + + @doc """ + Enqueues work generated by the federation module. + """ + @spec enqueue(module(), keyword()) :: :ok + def enqueue(module, args), do: PleromaJobQueue.enqueue(:federation_outgoing, module, args) + + @doc """ + Enqueue publishing a single activity. + """ + @spec enqueue_one(module(), Map.t()) :: :ok + def enqueue_one(module, %{} = args), do: enqueue(module, [:publish_one, args]) +end -- cgit v1.2.3 From ef1f9e8d4e4bd2bae0483523c7d31a20c086207e Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 03:09:05 +0000 Subject: activitypub: split out outgoing federation into a federation module --- lib/pleroma/web/activity_pub/activity_pub.ex | 87 ------------------ lib/pleroma/web/activity_pub/publisher.ex | 131 +++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 87 deletions(-) create mode 100644 lib/pleroma/web/activity_pub/publisher.ex (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 8f8c23a9b..11777c220 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -5,7 +5,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.Activity alias Pleroma.Conversation - alias Pleroma.Instances alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Object.Fetcher @@ -15,7 +14,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.User alias Pleroma.Web.ActivityPub.MRF alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.Federator alias Pleroma.Web.WebFinger import Ecto.Query @@ -24,8 +22,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do require Logger - @httpoison Application.get_env(:pleroma, :httpoison) - # For Announce activities, we filter the recipients based on following status for any actors # that match actual users. See issue #164 for more information about why this is necessary. defp get_recipients(%{"type" => "Announce"} = data) do @@ -961,89 +957,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - def should_federate?(inbox, public) do - if public do - true - else - inbox_info = URI.parse(inbox) - !Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host) - end - end - - def publish(actor, activity) do - remote_followers = - if actor.follower_address in activity.recipients do - {:ok, followers} = User.get_followers(actor) - followers |> Enum.filter(&(!&1.local)) - else - [] - end - - public = is_public?(activity) - - {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) - json = Jason.encode!(data) - - (Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers) - |> Enum.filter(fn user -> User.ap_enabled?(user) end) - |> Enum.map(fn %{info: %{source_data: data}} -> - (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] - end) - |> Enum.uniq() - |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) - |> Instances.filter_reachable() - |> Enum.each(fn {inbox, unreachable_since} -> - Federator.publish_single_ap(%{ - inbox: inbox, - json: json, - actor: actor, - id: activity.data["id"], - unreachable_since: unreachable_since - }) - end) - end - - def publish_one(%{inbox: inbox, json: json, actor: actor, id: id} = params) do - Logger.info("Federating #{id} to #{inbox}") - host = URI.parse(inbox).host - - digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64()) - - date = - NaiveDateTime.utc_now() - |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT") - - signature = - Pleroma.Web.HTTPSignatures.sign(actor, %{ - host: host, - "content-length": byte_size(json), - digest: digest, - date: date - }) - - with {:ok, %{status: code}} when code in 200..299 <- - result = - @httpoison.post( - inbox, - json, - [ - {"Content-Type", "application/activity+json"}, - {"Date", date}, - {"signature", signature}, - {"digest", digest} - ] - ) do - if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since], - do: Instances.set_reachable(inbox) - - result - else - {_post_result, response} -> - unless params[:unreachable_since], do: Instances.set_unreachable(inbox) - {:error, response} - end - end - # filter out broken threads def contain_broken_threads(%Activity{} = activity, %User{} = user) do entire_thread_visible_for_user?(activity, user) diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex new file mode 100644 index 000000000..ee9f0fdd3 --- /dev/null +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -0,0 +1,131 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Publisher do + alias Pleroma.Activity + alias Pleroma.Instances + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Web.ActivityPub.Visibility + + @behaviour Pleroma.Web.Federator.Publisher + + require Logger + + @httpoison Application.get_env(:pleroma, :httpoison) + + @moduledoc """ + ActivityPub outgoing federation module. + """ + + @doc """ + Determine if an activity can be represented by running it through Transmogrifier. + """ + def is_representable?(%Activity{} = activity) do + with %{} = _data <- Transmogrifier.prepare_outgoing(activity.data) do + true + else + _e -> false + end + end + + @doc """ + Publish a single message to a peer. Takes a struct with the following + parameters set: + + * `inbox`: the inbox to publish to + * `json`: the JSON message body representing the ActivityPub message + * `actor`: the actor which is signing the message + * `id`: the ActivityStreams URI of the message + """ + def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do + Logger.info("Federating #{id} to #{inbox}") + host = URI.parse(inbox).host + + digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64()) + + date = + NaiveDateTime.utc_now() + |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT") + + signature = + Pleroma.Web.HTTPSignatures.sign(actor, %{ + host: host, + "content-length": byte_size(json), + digest: digest, + date: date + }) + + with {:ok, %{status: code}} when code in 200..299 <- + result = + @httpoison.post( + inbox, + json, + [ + {"Content-Type", "application/activity+json"}, + {"Date", date}, + {"signature", signature}, + {"digest", digest} + ] + ) do + if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since], + do: Instances.set_reachable(inbox) + + result + else + {_post_result, response} -> + unless params[:unreachable_since], do: Instances.set_unreachable(inbox) + {:error, response} + end + end + + defp should_federate?(inbox, public) do + if public do + true + else + inbox_info = URI.parse(inbox) + !Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host) + end + end + + @doc """ + Publishes an activity to all relevant peers. + """ + def publish(%User{} = actor, %Activity{} = activity) do + remote_followers = + if actor.follower_address in activity.recipients do + {:ok, followers} = User.get_followers(actor) + followers |> Enum.filter(&(!&1.local)) + else + [] + end + + public = is_public?(activity) + + {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) + json = Jason.encode!(data) + + (Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers) + |> Enum.filter(fn user -> User.ap_enabled?(user) end) + |> Enum.map(fn %{info: %{source_data: data}} -> + (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] + end) + |> Enum.uniq() + |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) + |> Instances.filter_reachable() + |> Enum.each(fn {inbox, unreachable_since} -> + Pleroma.Web.Federator.Publisher.enqueue_one( + __MODULE__, + %{ + inbox: inbox, + json: json, + actor: actor, + id: activity.data["id"], + unreachable_since: unreachable_since + } + ) + end) + end +end -- cgit v1.2.3 From 0afc8d7856c9fe37de338d1e9365563d986c9319 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 03:43:53 +0000 Subject: federator: publisher: integrate job queue, simplify publish_one logic --- lib/pleroma/web/federator/publisher.ex | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index 36277fd7e..2e533ae94 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -3,6 +3,10 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Federator.Publisher do + alias Pleroma.Web.Federator.RetryQueue + + require Logger + @moduledoc """ Defines the contract used by federation implementations to publish messages to their peers. @@ -24,15 +28,26 @@ defmodule Pleroma.Web.Federator.Publisher do """ @callback publish(Pleroma.User.t(), Pleroma.Activity.t()) :: :ok | {:error, any()} - @doc """ - Enqueues work generated by the federation module. - """ - @spec enqueue(module(), keyword()) :: :ok - def enqueue(module, args), do: PleromaJobQueue.enqueue(:federation_outgoing, module, args) - @doc """ Enqueue publishing a single activity. """ @spec enqueue_one(module(), Map.t()) :: :ok - def enqueue_one(module, %{} = args), do: enqueue(module, [:publish_one, args]) + def enqueue_one(module, %{} = params), + do: PleromaJobQueue.enqueue(:federation_outgoing, __MODULE__, [:publish_one, module, params]) + + @spec perform(atom(), module(), any()) :: {:ok, any()} | {:error, any()} + def perform(:publish_one, module, params) do + case apply(module, :publish_one, [params]) do + {:ok, _} -> + :ok + + {:error, _} -> + RetryQueue.enqueue(params, module) + end + end + + def perform(type, _, _) do + Logger.debug("Unknown task: #{type}") + {:error, "Don't know what to do with this"} + end end -- cgit v1.2.3 From 10695a28d28d74f4b6b9bba20af66b506b662c07 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 03:55:17 +0000 Subject: federator: publisher: add publish() wrapper --- lib/pleroma/web/federator/publisher.ex | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index 2e533ae94..8777a3deb 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -3,6 +3,9 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Federator.Publisher do + alias Pleroma.Activity + alias Pleroma.Config + alias Pleroma.User alias Pleroma.Web.Federator.RetryQueue require Logger @@ -23,11 +26,6 @@ defmodule Pleroma.Web.Federator.Publisher do """ @callback publish_one(Map.t()) :: {:ok, Map.t()} | {:error, any()} - @doc """ - Relays an activity to all specified peers. - """ - @callback publish(Pleroma.User.t(), Pleroma.Activity.t()) :: :ok | {:error, any()} - @doc """ Enqueue publishing a single activity. """ @@ -50,4 +48,20 @@ defmodule Pleroma.Web.Federator.Publisher do Logger.debug("Unknown task: #{type}") {:error, "Don't know what to do with this"} end + + @doc """ + Relays an activity to all specified peers. + """ + @callback publish(Pleroma.User.t(), Pleroma.Activity.t()) :: :ok | {:error, any()} + + @spec publish(Pleroma.User.t(), Pleroma.Activity.t()) :: :ok + def publish(%User{} = user, %Activity{} = activity) do + Config.get([:instance, :federation_publisher_modules]) + |> Enum.each(fn module -> + Logger.info("Publishing #{activity.data["id"]} using #{inspect(module)}") + module.publish(user, activity) + end) + + :ok + end end -- cgit v1.2.3 From f7a6a37c4eb98e354fbcd98ea19c9207d891e993 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 03:56:49 +0000 Subject: federator: remove no longer used :publish_single_ap --- lib/pleroma/web/federator/federator.ex | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index 29e178ba9..d8534b365 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -42,10 +42,6 @@ defmodule Pleroma.Web.Federator do PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish, activity], priority) end - def publish_single_ap(params) do - PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_single_ap, params]) - end - def publish_single_websub(websub) do PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_single_websub, websub]) end @@ -157,16 +153,6 @@ defmodule Pleroma.Web.Federator do Salmon.send_to_user(params) end - def perform(:publish_single_ap, params) do - case ActivityPub.publish_one(params) do - {:ok, _} -> - :ok - - {:error, _} -> - RetryQueue.enqueue(params, ActivityPub) - end - end - def perform( :publish_single_websub, %{xml: _xml, topic: _topic, callback: _callback, secret: _secret} = params -- cgit v1.2.3 From 676752bb8367ec6b5831c7dbd2aad993b1fe45aa Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 03:57:10 +0000 Subject: federator: hook up Publisher.publish() --- lib/pleroma/web/federator/federator.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index d8534b365..ef2708c07 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Web.Federator do alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility + alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Federator.RetryQueue alias Pleroma.Web.OStatus alias Pleroma.Web.Salmon @@ -106,8 +107,7 @@ defmodule Pleroma.Web.Federator do end end - Logger.info(fn -> "Sending #{activity.data["id"]} out via AP" end) - Pleroma.Web.ActivityPub.ActivityPub.publish(actor, activity) + Publisher.publish(actor, activity) end end -- cgit v1.2.3 From 85377c0b67cf82fdf7e49608b94aec80ace74c4d Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 04:00:55 +0000 Subject: federator: move activitypub relaying to the AP publisher module --- lib/pleroma/web/activity_pub/publisher.ex | 7 +++++++ lib/pleroma/web/federator/federator.ex | 6 ------ 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index ee9f0fdd3..11e54b77d 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -4,8 +4,10 @@ defmodule Pleroma.Web.ActivityPub.Publisher do alias Pleroma.Activity + alias Pleroma.Config alias Pleroma.Instances alias Pleroma.User + alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Transmogrifier import Pleroma.Web.ActivityPub.Visibility @@ -104,6 +106,11 @@ defmodule Pleroma.Web.ActivityPub.Publisher do public = is_public?(activity) + if public && Config.get([:instance, :allow_relay]) do + Logger.info(fn -> "Relaying #{activity.data["id"]} out" end) + Relay.publish(activity) + end + {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) json = Jason.encode!(data) diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index ef2708c07..252d3b009 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -7,7 +7,6 @@ defmodule Pleroma.Web.Federator do alias Pleroma.Object.Containment alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility @@ -100,11 +99,6 @@ defmodule Pleroma.Web.Federator do Logger.info(fn -> "Sending #{activity.data["id"]} out via Salmon" end) Pleroma.Web.Salmon.publish(actor, activity) end - - if Keyword.get(Application.get_env(:pleroma, :instance), :allow_relay) do - Logger.info(fn -> "Relaying #{activity.data["id"]} out" end) - Relay.publish(activity) - end end Publisher.publish(actor, activity) -- cgit v1.2.3 From 69158f10652e735e3300335dba3856a0233da89f Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 04:04:50 +0000 Subject: ostatus: only as:Public activities are representable --- lib/pleroma/web/ostatus/ostatus.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 4744c6d83..61515b31e 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -16,6 +16,7 @@ defmodule Pleroma.Web.OStatus do alias Pleroma.Web alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.OStatus.DeleteHandler alias Pleroma.Web.OStatus.FollowHandler alias Pleroma.Web.OStatus.NoteHandler @@ -30,7 +31,7 @@ defmodule Pleroma.Web.OStatus do is_nil(object) -> false - object.data["type"] == "Note" -> + Visibility.is_public?(activity) && object.data["type"] == "Note" -> true true -> -- cgit v1.2.3 From 2aee62a4565d36983484ead9dda187e9e8188971 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 04:17:17 +0000 Subject: federator: publisher: only attempt publishing if we know the activity is representable --- lib/pleroma/web/federator/publisher.ex | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index 8777a3deb..67f4b7ba7 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -39,7 +39,7 @@ defmodule Pleroma.Web.Federator.Publisher do {:ok, _} -> :ok - {:error, _} -> + {:error, _e} -> RetryQueue.enqueue(params, module) end end @@ -58,8 +58,10 @@ defmodule Pleroma.Web.Federator.Publisher do def publish(%User{} = user, %Activity{} = activity) do Config.get([:instance, :federation_publisher_modules]) |> Enum.each(fn module -> - Logger.info("Publishing #{activity.data["id"]} using #{inspect(module)}") - module.publish(user, activity) + if module.is_representable?(activity) do + Logger.info("Publishing #{activity.data["id"]} using #{inspect(module)}") + module.publish(user, activity) + end end) :ok -- cgit v1.2.3 From 179293e51c2e381fdc15c0a291b735750f9cd656 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 04:27:01 +0000 Subject: salmon: refactor to work as a federator publishing module --- lib/pleroma/web/federator/federator.ex | 12 ------------ lib/pleroma/web/salmon/salmon.ex | 34 ++++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 24 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index 252d3b009..c9b245933 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -13,7 +13,6 @@ defmodule Pleroma.Web.Federator do alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Federator.RetryQueue alias Pleroma.Web.OStatus - alias Pleroma.Web.Salmon alias Pleroma.Web.WebFinger alias Pleroma.Web.Websub @@ -58,10 +57,6 @@ defmodule Pleroma.Web.Federator do PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:refresh_subscriptions]) end - def publish_single_salmon(params) do - PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_single_salmon, params]) - end - # Job Worker Callbacks def perform(:refresh_subscriptions) do @@ -95,9 +90,6 @@ defmodule Pleroma.Web.Federator do if OStatus.is_representable?(activity) do Logger.info(fn -> "Sending #{activity.data["id"]} out via WebSub" end) Websub.publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity) - - Logger.info(fn -> "Sending #{activity.data["id"]} out via Salmon" end) - Pleroma.Web.Salmon.publish(actor, activity) end end @@ -143,10 +135,6 @@ defmodule Pleroma.Web.Federator do end end - def perform(:publish_single_salmon, params) do - Salmon.send_to_user(params) - end - def perform( :publish_single_websub, %{xml: _xml, topic: _topic, callback: _callback, secret: _secret} = params diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 0a9e51656..7b59609c0 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -3,12 +3,17 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Salmon do + @behaviour Pleroma.Web.Federator.Publisher + @httpoison Application.get_env(:pleroma, :httpoison) use Bitwise + alias Pleroma.Activity alias Pleroma.Instances alias Pleroma.User + alias Pleroma.Web.ActivityPub.Visibility + alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.OStatus.ActivityRepresenter alias Pleroma.Web.XML @@ -165,12 +170,12 @@ defmodule Pleroma.Web.Salmon do end @doc "Pushes an activity to remote account." - def send_to_user(%{recipient: %{info: %{salmon: salmon}}} = params), - do: send_to_user(Map.put(params, :recipient, salmon)) + def publish_one(%{recipient: %{info: %{salmon: salmon}}} = params), + do: publish_one(Map.put(params, :recipient, salmon)) - def send_to_user(%{recipient: url, feed: feed, poster: poster} = params) when is_binary(url) do + def publish_one(%{recipient: url, feed: feed} = params) when is_binary(url) do with {:ok, %{status: code}} when code in 200..299 <- - poster.( + @httpoison.post( url, feed, [{"Content-Type", "application/magic-envelope+xml"}] @@ -184,11 +189,11 @@ defmodule Pleroma.Web.Salmon do e -> unless params[:unreachable_since], do: Instances.set_reachable(url) Logger.debug(fn -> "Pushing Salmon to #{url} failed, #{inspect(e)}" end) - :error + {:error, "Unreachable instance"} end end - def send_to_user(_), do: :noop + def publish_one(_), do: :noop @supported_activities [ "Create", @@ -199,13 +204,19 @@ defmodule Pleroma.Web.Salmon do "Delete" ] + def is_representable?(%Activity{data: %{"type" => type}} = activity) + when type in @supported_activities, + do: Visibility.is_public?(activity) + + def is_representable?(_), do: false + @doc """ Publishes an activity to remote accounts """ - @spec publish(User.t(), Pleroma.Activity.t(), Pleroma.HTTP.t()) :: none - def publish(user, activity, poster \\ &@httpoison.post/3) + @spec publish(User.t(), Pleroma.Activity.t()) :: none + def publish(user, activity) - def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity, poster) + def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity) when type in @supported_activities do feed = ActivityRepresenter.to_simple_form(activity, user, true) @@ -229,15 +240,14 @@ defmodule Pleroma.Web.Salmon do |> Enum.each(fn remote_user -> Logger.debug(fn -> "Sending Salmon to #{remote_user.ap_id}" end) - Pleroma.Web.Federator.publish_single_salmon(%{ + Publisher.enqueue_one(__MODULE__, %{ recipient: remote_user, feed: feed, - poster: poster, unreachable_since: reachable_urls_metadata[remote_user.info.salmon] }) end) end end - def publish(%{id: id}, _, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end) + def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end) end -- cgit v1.2.3 From fedaca15a3932897d86f73d836b6b6c2f7860f59 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 04:34:55 +0000 Subject: websub: adapt to work as a federator publishing module --- lib/pleroma/web/federator/federator.ex | 4 ---- lib/pleroma/web/websub/websub.ex | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index c9b245933..9a377da68 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -41,10 +41,6 @@ defmodule Pleroma.Web.Federator do PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish, activity], priority) end - def publish_single_websub(websub) do - PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_single_websub, websub]) - end - def verify_websub(websub) do PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:verify_websub, websub]) end diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index 3ffa6b416..1fb993282 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -4,10 +4,13 @@ defmodule Pleroma.Web.Websub do alias Ecto.Changeset + alias Pleroma.Activity alias Pleroma.Instances alias Pleroma.Repo + alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Endpoint alias Pleroma.Web.Federator + alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.OStatus alias Pleroma.Web.OStatus.FeedRepresenter alias Pleroma.Web.Router.Helpers @@ -18,6 +21,8 @@ defmodule Pleroma.Web.Websub do import Ecto.Query + @behaviour Pleroma.Web.Federator.Publisher + @httpoison Application.get_env(:pleroma, :httpoison) def verify(subscription, getter \\ &@httpoison.get/3) do @@ -56,6 +61,13 @@ defmodule Pleroma.Web.Websub do "Undo", "Delete" ] + + def is_representable?(%Activity{data: %{"type" => type}} = activity) + when type in @supported_activities, + do: Visibility.is_public?(activity) + + def is_representable?(_), do: false + def publish(topic, user, %{data: %{"type" => type}} = activity) when type in @supported_activities do response = @@ -88,12 +100,14 @@ defmodule Pleroma.Web.Websub do unreachable_since: reachable_callbacks_metadata[sub.callback] } - Federator.publish_single_websub(data) + Publisher.enqueue_one(__MODULE__, data) end) end def publish(_, _, _), do: "" + def publish(actor, activity), do: publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity) + def sign(secret, doc) do :crypto.hmac(:sha, secret, to_string(doc)) |> Base.encode16() |> String.downcase() end -- cgit v1.2.3 From c23276a59aa57a89e27c2e2f46d701392917b9a0 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 05:01:00 +0000 Subject: activitypub: publisher: fixups --- lib/pleroma/web/activity_pub/publisher.ex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 11e54b77d..5d72299a5 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -26,10 +26,11 @@ defmodule Pleroma.Web.ActivityPub.Publisher do Determine if an activity can be represented by running it through Transmogrifier. """ def is_representable?(%Activity{} = activity) do - with %{} = _data <- Transmogrifier.prepare_outgoing(activity.data) do + with {:ok, _data} <- Transmogrifier.prepare_outgoing(activity.data) do true else - _e -> false + _e -> + false end end -- cgit v1.2.3 From 55fa4b812a30feddc0c60d7ade03d50193871d22 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 05:01:15 +0000 Subject: federator: websub removal --- lib/pleroma/web/federator/federator.ex | 9 --------- 1 file changed, 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index 9a377da68..8621eda95 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -9,10 +9,8 @@ defmodule Pleroma.Web.Federator do alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Federator.RetryQueue - alias Pleroma.Web.OStatus alias Pleroma.Web.WebFinger alias Pleroma.Web.Websub @@ -82,13 +80,6 @@ defmodule Pleroma.Web.Federator do with actor when not is_nil(actor) <- User.get_cached_by_ap_id(activity.data["actor"]) do {:ok, actor} = WebFinger.ensure_keys_present(actor) - if Visibility.is_public?(activity) do - if OStatus.is_representable?(activity) do - Logger.info(fn -> "Sending #{activity.data["id"]} out via WebSub" end) - Websub.publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity) - end - end - Publisher.publish(actor, activity) end end -- cgit v1.2.3 From 80759f012eb2183bc24f84c4a1f2a5dbe94762ce Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 19:04:37 +0000 Subject: xml builder: properly escape quotes --- lib/xml_builder.ex | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/xml_builder.ex b/lib/xml_builder.ex index 88f8ce2a3..b58602c7b 100644 --- a/lib/xml_builder.ex +++ b/lib/xml_builder.ex @@ -35,6 +35,7 @@ defmodule Pleroma.XmlBuilder do defp make_open_tag(tag, attributes) do attributes_string = for {attribute, value} <- attributes do + value = String.replace(value, "\"", """) "#{attribute}=\"#{value}\"" end |> Enum.join(" ") -- cgit v1.2.3 From 28f7f4c6dec681ae292767623cbad553b2a0f5b5 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 19:05:03 +0000 Subject: webfinger: build the response based on enabled federation modules --- lib/pleroma/web/activity_pub/publisher.ex | 11 ++++++ lib/pleroma/web/federator/publisher.ex | 13 ++++++ lib/pleroma/web/salmon/salmon.ex | 14 +++++++ lib/pleroma/web/web_finger/web_finger.ex | 66 +++++++++---------------------- lib/pleroma/web/websub/websub.ex | 15 +++++++ 5 files changed, 71 insertions(+), 48 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 5d72299a5..5c97485c8 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -136,4 +136,15 @@ defmodule Pleroma.Web.ActivityPub.Publisher do ) end) end + + def gather_webfinger_links(%User{} = user) do + [ + %{"rel" => "self", "type" => "application/activity+json", "href" => user.ap_id}, + %{ + "rel" => "self", + "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", + "href" => user.ap_id + } + ] + end end diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index 67f4b7ba7..112a0574f 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -66,4 +66,17 @@ defmodule Pleroma.Web.Federator.Publisher do :ok end + + @doc """ + Gathers links used by an outgoing federation module for WebFinger output. + """ + @callback gather_webfinger_links(Pleroma.User.t()) :: list() + + @spec gather_webfinger_links(Pleroma.User.t()) :: list() + def gather_webfinger_links(%User{} = user) do + Config.get([:instance, :federation_publisher_modules]) + |> Enum.reduce([], fn module, links -> + links ++ module.gather_webfinger_links(user) + end) + end end diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 7b59609c0..92e85b5e9 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -14,6 +14,7 @@ defmodule Pleroma.Web.Salmon do alias Pleroma.User alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Federator.Publisher + alias Pleroma.Web.OStatus alias Pleroma.Web.OStatus.ActivityRepresenter alias Pleroma.Web.XML @@ -250,4 +251,17 @@ defmodule Pleroma.Web.Salmon do end def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end) + + def gather_webfinger_links(%User{} = user) do + {:ok, _private, public} = keys_from_pem(user.info.keys) + magic_key = encode_key(public) + + [ + %{"rel" => "salmon", "href" => OStatus.salmon_path(user)}, + %{ + "rel" => "magic-public-key", + "href" => "data:application/magic-public-key,#{magic_key}" + } + ] + end end diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex index a3b0bf999..3a3b98a10 100644 --- a/lib/pleroma/web/web_finger/web_finger.ex +++ b/lib/pleroma/web/web_finger/web_finger.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Web.WebFinger do alias Pleroma.User alias Pleroma.Web - alias Pleroma.Web.OStatus + alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Salmon alias Pleroma.Web.XML alias Pleroma.XmlBuilder @@ -50,70 +50,40 @@ defmodule Pleroma.Web.WebFinger do end end + defp gather_links(%User{} = user) do + [ + %{ + "rel" => "http://webfinger.net/rel/profile-page", + "type" => "text/html", + "href" => user.ap_id + } + ] ++ Publisher.gather_webfinger_links(user) + end + def represent_user(user, "JSON") do {:ok, user} = ensure_keys_present(user) - {:ok, _private, public} = Salmon.keys_from_pem(user.info.keys) - magic_key = Salmon.encode_key(public) %{ "subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", "aliases" => [user.ap_id], - "links" => [ - %{ - "rel" => "http://schemas.google.com/g/2010#updates-from", - "type" => "application/atom+xml", - "href" => OStatus.feed_path(user) - }, - %{ - "rel" => "http://webfinger.net/rel/profile-page", - "type" => "text/html", - "href" => user.ap_id - }, - %{"rel" => "salmon", "href" => OStatus.salmon_path(user)}, - %{ - "rel" => "magic-public-key", - "href" => "data:application/magic-public-key,#{magic_key}" - }, - %{"rel" => "self", "type" => "application/activity+json", "href" => user.ap_id}, - %{ - "rel" => "self", - "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", - "href" => user.ap_id - }, - %{ - "rel" => "http://ostatus.org/schema/1.0/subscribe", - "template" => OStatus.remote_follow_path() - } - ] + "links" => gather_links(user) } end def represent_user(user, "XML") do {:ok, user} = ensure_keys_present(user) - {:ok, _private, public} = Salmon.keys_from_pem(user.info.keys) - magic_key = Salmon.encode_key(public) + + links = + gather_links(user) + |> Enum.map(fn link -> {:Link, link} end) { :XRD, %{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"}, [ {:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"}, - {:Alias, user.ap_id}, - {:Link, - %{ - rel: "http://schemas.google.com/g/2010#updates-from", - type: "application/atom+xml", - href: OStatus.feed_path(user) - }}, - {:Link, - %{rel: "http://webfinger.net/rel/profile-page", type: "text/html", href: user.ap_id}}, - {:Link, %{rel: "salmon", href: OStatus.salmon_path(user)}}, - {:Link, - %{rel: "magic-public-key", href: "data:application/magic-public-key,#{magic_key}"}}, - {:Link, %{rel: "self", type: "application/activity+json", href: user.ap_id}}, - {:Link, - %{rel: "http://ostatus.org/schema/1.0/subscribe", template: OStatus.remote_follow_path()}} - ] + {:Alias, user.ap_id} + ] ++ links } |> XmlBuilder.to_doc() end diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index 1fb993282..2ce6dcc19 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.Websub do alias Pleroma.Activity alias Pleroma.Instances alias Pleroma.Repo + alias Pleroma.User alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Endpoint alias Pleroma.Web.Federator @@ -313,4 +314,18 @@ defmodule Pleroma.Web.Websub do {:error, response} end end + + def gather_webfinger_links(%User{} = user) do + [ + %{ + "rel" => "http://schemas.google.com/g/2010#updates-from", + "type" => "application/atom+xml", + "href" => OStatus.feed_path(user) + }, + %{ + "rel" => "http://ostatus.org/schema/1.0/subscribe", + "template" => OStatus.remote_follow_path() + } + ] + end end -- cgit v1.2.3 From 44b182732efe2d8571aa54e6062637e7e42021ce Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 19:15:29 +0000 Subject: nodeinfo: gather supported protocol names from federation modules --- lib/pleroma/web/activity_pub/publisher.ex | 2 ++ lib/pleroma/web/federator/publisher.ex | 13 +++++++++++++ lib/pleroma/web/nodeinfo/nodeinfo_controller.ex | 3 ++- lib/pleroma/web/salmon/salmon.ex | 2 ++ lib/pleroma/web/websub/websub.ex | 2 ++ 5 files changed, 21 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 5c97485c8..8e3af0a81 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -147,4 +147,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do } ] end + + def gather_nodeinfo_protocol_names, do: ["activitypub"] end diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index 112a0574f..916bcdcba 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -79,4 +79,17 @@ defmodule Pleroma.Web.Federator.Publisher do links ++ module.gather_webfinger_links(user) end) end + + @doc """ + Gathers nodeinfo protocol names supported by the federation module. + """ + @callback gather_nodeinfo_protocol_names() :: list() + + @spec gather_nodeinfo_protocol_names() :: list() + def gather_nodeinfo_protocol_names do + Config.get([:instance, :federation_publisher_modules]) + |> Enum.reduce([], fn module, links -> + links ++ module.gather_nodeinfo_protocol_names() + end) + end end diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex index 216a962bd..3bf2a0fbc 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do alias Pleroma.User alias Pleroma.Web alias Pleroma.Web.ActivityPub.MRF + alias Pleroma.Web.Federator.Publisher plug(Pleroma.Web.FederatingPlug) @@ -137,7 +138,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do name: Pleroma.Application.name() |> String.downcase(), version: Pleroma.Application.version() }, - protocols: ["ostatus", "activitypub"], + protocols: Publisher.gather_nodeinfo_protocol_names(), services: %{ inbound: [], outbound: [] diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 92e85b5e9..42709ab47 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -264,4 +264,6 @@ defmodule Pleroma.Web.Salmon do } ] end + + def gather_nodeinfo_protocol_names, do: [] end diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index 2ce6dcc19..7ad0414ab 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -328,4 +328,6 @@ defmodule Pleroma.Web.Websub do } ] end + + def gather_nodeinfo_protocol_names, do: ["ostatus"] end -- cgit v1.2.3 From 7701a4c84191a445e0b62e8a241e8af86f86e16a Mon Sep 17 00:00:00 2001 From: Sachin Joshi Date: Mon, 13 May 2019 23:50:33 +0545 Subject: Make irreversible field default to false in filters --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 956736780..fd595031d 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1536,7 +1536,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do user_id: user.id, phrase: phrase, context: context, - hide: Map.get(params, "irreversible", nil), + hide: Map.get(params, "irreversible", false), whole_word: Map.get(params, "boolean", true) # expires_at } -- cgit v1.2.3 From a2be420f940fb8f181feeb9b0fb9759d433dcae1 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Mon, 13 May 2019 18:35:45 +0000 Subject: differences_in_mastoapi_responses.md: fullname & bio are optionnal [ci skip] --- lib/mix/tasks/pleroma/user.ex | 2 +- lib/pleroma/plugs/oauth_plug.ex | 48 +++++++++++++++--- lib/pleroma/plugs/rate_limit_plug.ex | 36 ++++++++++++++ lib/pleroma/user.ex | 11 +++-- lib/pleroma/user/info.ex | 30 +++++++----- lib/pleroma/web/admin_api/admin_api_controller.ex | 2 +- lib/pleroma/web/auth/pleroma_authenticator.ex | 2 +- .../web/mastodon_api/mastodon_api_controller.ex | 57 ++++++++++++++++++++++ lib/pleroma/web/oauth/app.ex | 1 + lib/pleroma/web/oauth/authorization.ex | 39 +++++++++++---- lib/pleroma/web/oauth/oauth_controller.ex | 22 +++++++++ lib/pleroma/web/oauth/token.ex | 11 +++-- lib/pleroma/web/router.ex | 2 + lib/pleroma/web/twitter_api/twitter_api.ex | 29 ++++++----- .../web/twitter_api/twitter_api_controller.ex | 2 +- 15 files changed, 239 insertions(+), 55 deletions(-) create mode 100644 lib/pleroma/plugs/rate_limit_plug.ex (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 6a83a8c0d..d130ff8c9 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -138,7 +138,7 @@ defmodule Mix.Tasks.Pleroma.User do bio: bio } - changeset = User.register_changeset(%User{}, params, confirmed: true) + changeset = User.register_changeset(%User{}, params, need_confirmation: false) {:ok, _user} = User.register(changeset) Mix.shell().info("User #{nickname} created") diff --git a/lib/pleroma/plugs/oauth_plug.ex b/lib/pleroma/plugs/oauth_plug.ex index 9d43732eb..86bc4aa3a 100644 --- a/lib/pleroma/plugs/oauth_plug.ex +++ b/lib/pleroma/plugs/oauth_plug.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Plugs.OAuthPlug do alias Pleroma.Repo alias Pleroma.User + alias Pleroma.Web.OAuth.App alias Pleroma.Web.OAuth.Token @realm_reg Regex.compile!("Bearer\:?\s+(.*)$", "i") @@ -22,18 +23,39 @@ defmodule Pleroma.Plugs.OAuthPlug do |> assign(:token, token_record) |> assign(:user, user) else - _ -> conn + _ -> + # token found, but maybe only with app + with {:ok, app, token_record} <- fetch_app_and_token(access_token) do + conn + |> assign(:token, token_record) + |> assign(:app, app) + else + _ -> conn + end end end def call(conn, _) do - with {:ok, token_str} <- fetch_token_str(conn), - {:ok, user, token_record} <- fetch_user_and_token(token_str) do - conn - |> assign(:token, token_record) - |> assign(:user, user) - else - _ -> conn + case fetch_token_str(conn) do + {:ok, token} -> + with {:ok, user, token_record} <- fetch_user_and_token(token) do + conn + |> assign(:token, token_record) + |> assign(:user, user) + else + _ -> + # token found, but maybe only with app + with {:ok, app, token_record} <- fetch_app_and_token(token) do + conn + |> assign(:token, token_record) + |> assign(:app, app) + else + _ -> conn + end + end + + _ -> + conn end end @@ -54,6 +76,16 @@ defmodule Pleroma.Plugs.OAuthPlug do end end + @spec fetch_app_and_token(String.t()) :: {:ok, App.t(), Token.t()} | nil + defp fetch_app_and_token(token) do + query = + from(t in Token, where: t.token == ^token, join: app in assoc(t, :app), preload: [app: app]) + + with %Token{app: app} = token_record <- Repo.one(query) do + {:ok, app, token_record} + end + end + # Gets token from session by :oauth_token key # @spec fetch_token_from_session(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()} diff --git a/lib/pleroma/plugs/rate_limit_plug.ex b/lib/pleroma/plugs/rate_limit_plug.ex new file mode 100644 index 000000000..466f64a79 --- /dev/null +++ b/lib/pleroma/plugs/rate_limit_plug.ex @@ -0,0 +1,36 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Plugs.RateLimitPlug do + import Phoenix.Controller, only: [json: 2] + import Plug.Conn + + def init(opts), do: opts + + def call(conn, opts) do + enabled? = Pleroma.Config.get([:app_account_creation, :enabled]) + + case check_rate(conn, Map.put(opts, :enabled, enabled?)) do + {:ok, _count} -> conn + {:error, _count} -> render_error(conn) + %Plug.Conn{} = conn -> conn + end + end + + defp check_rate(conn, %{enabled: true} = opts) do + max_requests = opts[:max_requests] + bucket_name = conn.remote_ip |> Tuple.to_list() |> Enum.join(".") + + ExRated.check_rate(bucket_name, opts[:interval] * 1000, max_requests) + end + + defp check_rate(conn, _), do: conn + + defp render_error(conn) do + conn + |> put_status(:forbidden) + |> json(%{error: "Rate limit exceeded."}) + |> halt() + end +end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 427400aa1..474de9ba5 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -204,14 +204,15 @@ defmodule Pleroma.User do end def register_changeset(struct, params \\ %{}, opts \\ []) do - confirmation_status = - if opts[:confirmed] || !Pleroma.Config.get([:instance, :account_activation_required]) do - :confirmed + need_confirmation? = + if is_nil(opts[:need_confirmation]) do + Pleroma.Config.get([:instance, :account_activation_required]) else - :unconfirmed + opts[:need_confirmation] end - info_change = User.Info.confirmation_changeset(%User.Info{}, confirmation_status) + info_change = + User.Info.confirmation_changeset(%User.Info{}, need_confirmation: need_confirmation?) changeset = struct diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 1b81619ce..5a50ee639 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -8,6 +8,8 @@ defmodule Pleroma.User.Info do alias Pleroma.User.Info + @type t :: %__MODULE__{} + embedded_schema do field(:banner, :map, default: %{}) field(:background, :map, default: %{}) @@ -210,21 +212,23 @@ defmodule Pleroma.User.Info do ]) end - def confirmation_changeset(info, :confirmed) do - confirmation_changeset(info, %{ - confirmation_pending: false, - confirmation_token: nil - }) - end + @spec confirmation_changeset(Info.t(), keyword()) :: Ecto.Changerset.t() + def confirmation_changeset(info, opts) do + need_confirmation? = Keyword.get(opts, :need_confirmation) - def confirmation_changeset(info, :unconfirmed) do - confirmation_changeset(info, %{ - confirmation_pending: true, - confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64() - }) - end + params = + if need_confirmation? do + %{ + confirmation_pending: true, + confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64() + } + else + %{ + confirmation_pending: false, + confirmation_token: nil + } + end - def confirmation_changeset(info, params) do cast(info, params, [:confirmation_pending, :confirmation_token]) end diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index b553d96a8..e00b33aba 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -59,7 +59,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do bio: "." } - changeset = User.register_changeset(%User{}, user_data, confirmed: true) + changeset = User.register_changeset(%User{}, user_data, need_confirmation: false) {:ok, user} = User.register(changeset) conn diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex index dd79cdcf7..c4a6fce08 100644 --- a/lib/pleroma/web/auth/pleroma_authenticator.ex +++ b/lib/pleroma/web/auth/pleroma_authenticator.ex @@ -74,7 +74,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do password_confirmation: random_password }, external: true, - confirmed: true + need_confirmation: false ) |> Repo.insert(), {:ok, _} <- diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index fd595031d..defd88a44 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -39,12 +39,22 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.Web.OAuth.Authorization alias Pleroma.Web.OAuth.Scopes alias Pleroma.Web.OAuth.Token + alias Pleroma.Web.TwitterAPI.TwitterAPI alias Pleroma.Web.ControllerHelper import Ecto.Query require Logger + plug( + Pleroma.Plugs.RateLimitPlug, + %{ + max_requests: Config.get([:app_account_creation, :max_requests]), + interval: Config.get([:app_account_creation, :interval]) + } + when action in [:account_register] + ) + @httpoison Application.get_env(:pleroma, :httpoison) @local_mastodon_name "Mastodon-Local" @@ -1693,6 +1703,53 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end + def account_register( + %{assigns: %{app: app}} = conn, + %{"username" => nickname, "email" => _, "password" => _, "agreement" => true} = params + ) do + params = + params + |> Map.take([ + "email", + "captcha_solution", + "captcha_token", + "captcha_answer_data", + "token", + "password" + ]) + |> Map.put("nickname", nickname) + |> Map.put("fullname", params["fullname"] || nickname) + |> Map.put("bio", params["bio"] || "") + |> Map.put("confirm", params["password"]) + + with {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true), + {:ok, token} <- Token.create_token(app, user, %{scopes: app.scopes}) do + json(conn, %{ + token_type: "Bearer", + access_token: token.token, + scope: app.scopes, + created_at: Token.Utils.format_created_at(token) + }) + else + {:error, errors} -> + conn + |> put_status(400) + |> json(Jason.encode!(errors)) + end + end + + def account_register(%{assigns: %{app: _app}} = conn, _params) do + conn + |> put_status(400) + |> json(%{error: "Missing parameters"}) + end + + def account_register(conn, _) do + conn + |> put_status(403) + |> json(%{error: "Invalid credentials"}) + end + def conversations(%{assigns: %{user: user}} = conn, params) do participations = Participation.for_user_with_last_activity_id(user, params) diff --git a/lib/pleroma/web/oauth/app.ex b/lib/pleroma/web/oauth/app.ex index bccc2ac96..ddcdb1871 100644 --- a/lib/pleroma/web/oauth/app.ex +++ b/lib/pleroma/web/oauth/app.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.App do import Ecto.Changeset @type t :: %__MODULE__{} + schema "apps" do field(:client_name, :string) field(:redirect_uris, :string) diff --git a/lib/pleroma/web/oauth/authorization.ex b/lib/pleroma/web/oauth/authorization.ex index ca3901cc4..b47688de1 100644 --- a/lib/pleroma/web/oauth/authorization.ex +++ b/lib/pleroma/web/oauth/authorization.ex @@ -14,6 +14,7 @@ defmodule Pleroma.Web.OAuth.Authorization do import Ecto.Query @type t :: %__MODULE__{} + schema "oauth_authorizations" do field(:token, :string) field(:scopes, {:array, :string}, default: []) @@ -25,28 +26,45 @@ defmodule Pleroma.Web.OAuth.Authorization do timestamps() end + @spec create_authorization(App.t(), User.t() | %{}, [String.t()] | nil) :: + {:ok, Authorization.t()} | {:error, Changeset.t()} def create_authorization(%App{} = app, %User{} = user, scopes \\ nil) do - scopes = scopes || app.scopes - token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false) - - authorization = %Authorization{ - token: token, - used: false, + %{ + scopes: scopes || app.scopes, user_id: user.id, - app_id: app.id, - scopes: scopes, - valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10) + app_id: app.id } + |> create_changeset() + |> Repo.insert() + end + + @spec create_changeset(map()) :: Changeset.t() + def create_changeset(attrs \\ %{}) do + %Authorization{} + |> cast(attrs, [:user_id, :app_id, :scopes, :valid_until]) + |> validate_required([:app_id, :scopes]) + |> add_token() + |> add_lifetime() + end + + defp add_token(changeset) do + token = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false) + put_change(changeset, :token, token) + end - Repo.insert(authorization) + defp add_lifetime(changeset) do + put_change(changeset, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10)) end + @spec use_changeset(Authtorizatiton.t(), map()) :: Changeset.t() def use_changeset(%Authorization{} = auth, params) do auth |> cast(params, [:used]) |> validate_required([:used]) end + @spec use_token(Authorization.t()) :: + {:ok, Authorization.t()} | {:error, Changeset.t()} | {:error, String.t()} def use_token(%Authorization{used: false, valid_until: valid_until} = auth) do if NaiveDateTime.diff(NaiveDateTime.utc_now(), valid_until) < 0 do Repo.update(use_changeset(auth, %{used: true})) @@ -57,6 +75,7 @@ defmodule Pleroma.Web.OAuth.Authorization do def use_token(%Authorization{used: true}), do: {:error, "already used"} + @spec delete_user_authorizations(User.t()) :: {integer(), any()} def delete_user_authorizations(%User{id: user_id}) do from( a in Pleroma.Web.OAuth.Authorization, diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 8ee0da667..862b8f8c9 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -218,6 +218,28 @@ defmodule Pleroma.Web.OAuth.OAuthController do token_exchange(conn, params) end + def token_exchange(conn, %{"grant_type" => "client_credentials"} = params) do + with %App{} = app <- get_app_from_request(conn, params), + {:ok, auth} <- Authorization.create_authorization(app, %User{}), + {:ok, token} <- Token.exchange_token(app, auth), + {:ok, inserted_at} <- DateTime.from_naive(token.inserted_at, "Etc/UTC") do + response = %{ + token_type: "Bearer", + access_token: token.token, + refresh_token: token.refresh_token, + created_at: DateTime.to_unix(inserted_at), + expires_in: 60 * 10, + scope: Enum.join(token.scopes, " ") + } + + json(conn, response) + else + _error -> + put_status(conn, 400) + |> json(%{error: "Invalid credentials"}) + end + end + # Bad request def token_exchange(conn, params), do: bad_request(conn, params) diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex index 4e5d1d118..ef047d565 100644 --- a/lib/pleroma/web/oauth/token.ex +++ b/lib/pleroma/web/oauth/token.ex @@ -45,12 +45,16 @@ defmodule Pleroma.Web.OAuth.Token do |> Repo.find_resource() end + @spec exchange_token(App.t(), Authorization.t()) :: + {:ok, Token.t()} | {:error, Changeset.t()} def exchange_token(app, auth) do with {:ok, auth} <- Authorization.use_token(auth), true <- auth.app_id == app.id do + user = if auth.user_id, do: User.get_cached_by_id(auth.user_id), else: %User{} + create_token( app, - User.get_cached_by_id(auth.user_id), + user, %{scopes: auth.scopes} ) end @@ -81,12 +85,13 @@ defmodule Pleroma.Web.OAuth.Token do |> validate_required([:valid_until]) end + @spec create_token(App.t(), User.t(), map()) :: {:ok, Token} | {:error, Changeset.t()} def create_token(%App{} = app, %User{} = user, attrs \\ %{}) do %__MODULE__{user_id: user.id, app_id: app.id} |> cast(%{scopes: attrs[:scopes] || app.scopes}, [:scopes]) - |> validate_required([:scopes, :user_id, :app_id]) + |> validate_required([:scopes, :app_id]) |> put_valid_until(attrs) - |> put_token + |> put_token() |> put_refresh_token(attrs) |> Repo.insert() end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 8b84fbbad..51146d010 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -385,6 +385,8 @@ defmodule Pleroma.Web.Router do scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:api) + post("/accounts", MastodonAPIController, :account_register) + get("/instance", MastodonAPIController, :masto_instance) get("/instance/peers", MastodonAPIController, :peers) post("/apps", MastodonAPIController, :create_app) diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 3a7774647..1362ef57c 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -128,7 +128,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do end end - def register_user(params) do + def register_user(params, opts \\ []) do token = params["token"] params = %{ @@ -162,13 +162,22 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do # I have no idea how this error handling works {:error, %{error: Jason.encode!(%{captcha: [error]})}} else - registrations_open = Pleroma.Config.get([:instance, :registrations_open]) - registration_process(registrations_open, params, token) + registration_process( + params, + %{ + registrations_open: Pleroma.Config.get([:instance, :registrations_open]), + token: token + }, + opts + ) end end - defp registration_process(registration_open, params, token) - when registration_open == false or is_nil(registration_open) do + defp registration_process(params, %{registrations_open: true}, opts) do + create_user(params, opts) + end + + defp registration_process(params, %{token: token}, opts) do invite = unless is_nil(token) do Repo.get_by(UserInviteToken, %{token: token}) @@ -182,19 +191,15 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do invite when valid_invite? -> UserInviteToken.update_usage!(invite) - create_user(params) + create_user(params, opts) _ -> {:error, "Expired token"} end end - defp registration_process(true, params, _token) do - create_user(params) - end - - defp create_user(params) do - changeset = User.register_changeset(%User{}, params) + defp create_user(params, opts) do + changeset = User.register_changeset(%User{}, params, opts) case User.register(changeset) do {:ok, user} -> diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 21e6c555a..3c5a70be9 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -440,7 +440,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do true <- user.local, true <- user.info.confirmation_pending, true <- user.info.confirmation_token == token, - info_change <- User.Info.confirmation_changeset(user.info, :confirmed), + info_change <- User.Info.confirmation_changeset(user.info, need_confirmation: false), changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_change), {:ok, _} <- User.update_and_set_cache(changeset) do conn -- cgit v1.2.3 From 498bfdf403b6d20f92363a4b650dd0b926351e17 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 13 May 2019 15:37:38 -0500 Subject: Switch to Jason over Poison --- lib/mix/tasks/pleroma/emoji.ex | 12 ++++++------ lib/pleroma/captcha/kocaptcha.ex | 2 +- lib/pleroma/uploaders/swift/keystone.ex | 4 ++-- lib/pleroma/web/activity_pub/utils.ex | 2 +- lib/pleroma/web/federator/federator.ex | 2 +- lib/pleroma/web/oauth/oauth_controller.ex | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/mix/tasks/pleroma/emoji.ex b/lib/mix/tasks/pleroma/emoji.ex index 5cb54c3ca..d2ddf450a 100644 --- a/lib/mix/tasks/pleroma/emoji.ex +++ b/lib/mix/tasks/pleroma/emoji.ex @@ -137,7 +137,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do ]) ) - files = Tesla.get!(client(), files_url).body |> Poison.decode!() + files = Tesla.get!(client(), files_url).body |> Jason.decode!() IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name])) @@ -239,7 +239,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do emoji_map = Pleroma.Emoji.make_shortcode_to_file_map(tmp_pack_dir, exts) - File.write!(files_name, Poison.encode!(emoji_map, pretty: true)) + File.write!(files_name, Jason.encode!(emoji_map, pretty: true)) IO.puts(""" @@ -248,11 +248,11 @@ defmodule Mix.Tasks.Pleroma.Emoji do """) if File.exists?("index.json") do - existing_data = File.read!("index.json") |> Poison.decode!() + existing_data = File.read!("index.json") |> Jason.decode!() File.write!( "index.json", - Poison.encode!( + Jason.encode!( Map.merge( existing_data, pack_json @@ -263,14 +263,14 @@ defmodule Mix.Tasks.Pleroma.Emoji do IO.puts("index.json file has been update with the #{name} pack") else - File.write!("index.json", Poison.encode!(pack_json, pretty: true)) + File.write!("index.json", Jason.encode!(pack_json, pretty: true)) IO.puts("index.json has been created with the #{name} pack") end end defp fetch_manifest(from) do - Poison.decode!( + Jason.decode!( if String.starts_with?(from, "http") do Tesla.get!(client(), from).body else diff --git a/lib/pleroma/captcha/kocaptcha.ex b/lib/pleroma/captcha/kocaptcha.ex index 61688e778..18931d5a0 100644 --- a/lib/pleroma/captcha/kocaptcha.ex +++ b/lib/pleroma/captcha/kocaptcha.ex @@ -15,7 +15,7 @@ defmodule Pleroma.Captcha.Kocaptcha do %{error: "Kocaptcha service unavailable"} {:ok, res} -> - json_resp = Poison.decode!(res.body) + json_resp = Jason.decode!(res.body) %{ type: :kocaptcha, diff --git a/lib/pleroma/uploaders/swift/keystone.ex b/lib/pleroma/uploaders/swift/keystone.ex index 3046cdbd2..dd44c7561 100644 --- a/lib/pleroma/uploaders/swift/keystone.ex +++ b/lib/pleroma/uploaders/swift/keystone.ex @@ -14,7 +14,7 @@ defmodule Pleroma.Uploaders.Swift.Keystone do def process_response_body(body) do body - |> Poison.decode!() + |> Jason.decode!() end def get_token do @@ -38,7 +38,7 @@ defmodule Pleroma.Uploaders.Swift.Keystone do end def make_auth_body(username, password, tenant) do - Poison.encode!(%{ + Jason.encode!(%{ :auth => %{ :passwordCredentials => %{ :username => username, diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 581b9d1ab..236d1b4ac 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -682,7 +682,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do """ def fetch_ordered_collection(from, pages_left, acc \\ []) do with {:ok, response} <- Tesla.get(from), - {:ok, collection} <- Poison.decode(response.body) do + {:ok, collection} <- Jason.decode(response.body) do case collection["type"] do "OrderedCollection" -> # If we've encountered the OrderedCollection and not the page, diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index 8621eda95..169fdf4dc 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -117,7 +117,7 @@ defmodule Pleroma.Web.Federator do _e -> # Just drop those for now Logger.info("Unhandled activity") - Logger.info(Poison.encode!(params, pretty: 2)) + Logger.info(Jason.encode!(params, pretty: true)) :error end end diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 862b8f8c9..4ee8339e2 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -274,7 +274,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do auth_attrs |> Map.delete("scopes") |> Map.put("scope", scope) - |> Poison.encode!() + |> Jason.encode!() params = auth_attrs @@ -338,7 +338,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do end defp callback_params(%{"state" => state} = params) do - Map.merge(params, Poison.decode!(state)) + Map.merge(params, Jason.decode!(state)) end def registration_details(conn, %{"authorization" => auth_attrs}) do -- cgit v1.2.3 From bbacdf235299e0c26c61f9b4784d4638093be343 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 13 May 2019 15:52:16 -0500 Subject: Bump Mastodon API level --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index defd88a44..87e597074 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -178,7 +178,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end - @mastodon_api_level "2.6.5" + @mastodon_api_level "2.7.2" def masto_instance(conn, _params) do instance = Config.get(:instance) -- cgit v1.2.3