Merge branch 'ostatus-controller-no-auth-check-on-non-federating-instances' into 'develop'
OStatus / Static FE access control fixes See merge request pleroma/pleroma!3053
This commit is contained in:
commit
25832534cd
|
@ -52,6 +52,7 @@ switched to a new configuration mechanism, however it was not officially removed
|
||||||
- Allow sending out emails again.
|
- Allow sending out emails again.
|
||||||
- Allow sending chat messages to yourself.
|
- Allow sending chat messages to yourself.
|
||||||
- Fix remote users with a whitespace name.
|
- Fix remote users with a whitespace name.
|
||||||
|
- OStatus / static FE endpoints: fixed inaccessibility for anonymous users on non-federating instances, switched to handling per `:restrict_unauthenticated` setting.
|
||||||
|
|
||||||
## Unreleased (Patch)
|
## Unreleased (Patch)
|
||||||
|
|
||||||
|
|
|
@ -44,29 +44,30 @@ def is_direct?(activity) do
|
||||||
def is_list?(%{data: %{"listMessage" => _}}), do: true
|
def is_list?(%{data: %{"listMessage" => _}}), do: true
|
||||||
def is_list?(_), do: false
|
def is_list?(_), do: false
|
||||||
|
|
||||||
@spec visible_for_user?(Activity.t(), User.t() | nil) :: boolean()
|
@spec visible_for_user?(Activity.t() | nil, User.t() | nil) :: boolean()
|
||||||
def visible_for_user?(%{actor: ap_id}, %User{ap_id: ap_id}), do: true
|
def visible_for_user?(%Activity{actor: ap_id}, %User{ap_id: ap_id}), do: true
|
||||||
|
|
||||||
def visible_for_user?(nil, _), do: false
|
def visible_for_user?(nil, _), do: false
|
||||||
|
|
||||||
def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false
|
def visible_for_user?(%Activity{data: %{"listMessage" => _}}, nil), do: false
|
||||||
|
|
||||||
def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{} = user) do
|
def visible_for_user?(
|
||||||
|
%Activity{data: %{"listMessage" => list_ap_id}} = activity,
|
||||||
|
%User{} = user
|
||||||
|
) do
|
||||||
user.ap_id in activity.data["to"] ||
|
user.ap_id in activity.data["to"] ||
|
||||||
list_ap_id
|
list_ap_id
|
||||||
|> Pleroma.List.get_by_ap_id()
|
|> Pleroma.List.get_by_ap_id()
|
||||||
|> Pleroma.List.member?(user)
|
|> Pleroma.List.member?(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def visible_for_user?(%{local: local} = activity, nil) do
|
def visible_for_user?(%Activity{} = activity, nil) do
|
||||||
cfg_key = if local, do: :local, else: :remote
|
if restrict_unauthenticated_access?(activity),
|
||||||
|
|
||||||
if Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key),
|
|
||||||
do: false,
|
do: false,
|
||||||
else: is_public?(activity)
|
else: is_public?(activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
def visible_for_user?(activity, user) do
|
def visible_for_user?(%Activity{} = activity, user) do
|
||||||
x = [user.ap_id | User.following(user)]
|
x = [user.ap_id | User.following(user)]
|
||||||
y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || [])
|
y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || [])
|
||||||
is_public?(activity) || Enum.any?(x, &(&1 in y))
|
is_public?(activity) || Enum.any?(x, &(&1 in y))
|
||||||
|
@ -82,6 +83,26 @@ def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def restrict_unauthenticated_access?(%Activity{local: local}) do
|
||||||
|
restrict_unauthenticated_access_to_activity?(local)
|
||||||
|
end
|
||||||
|
|
||||||
|
def restrict_unauthenticated_access?(%Object{} = object) do
|
||||||
|
object
|
||||||
|
|> Object.local?()
|
||||||
|
|> restrict_unauthenticated_access_to_activity?()
|
||||||
|
end
|
||||||
|
|
||||||
|
def restrict_unauthenticated_access?(%User{} = user) do
|
||||||
|
User.visible_for(user, _reading_user = nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_unauthenticated_access_to_activity?(local?) when is_boolean(local?) do
|
||||||
|
cfg_key = if local?, do: :local, else: :remote
|
||||||
|
|
||||||
|
Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key)
|
||||||
|
end
|
||||||
|
|
||||||
def get_visibility(object) do
|
def get_visibility(object) do
|
||||||
to = object.data["to"] || []
|
to = object.data["to"] || []
|
||||||
cc = object.data["cc"] || []
|
cc = object.data["cc"] || []
|
||||||
|
|
|
@ -10,14 +10,14 @@ defmodule Pleroma.Web.Feed.TagController do
|
||||||
alias Pleroma.Web.Feed.FeedView
|
alias Pleroma.Web.Feed.FeedView
|
||||||
|
|
||||||
def feed(conn, params) do
|
def feed(conn, params) do
|
||||||
unless Pleroma.Config.restrict_unauthenticated_access?(:activities, :local) do
|
if Config.get!([:instance, :public]) do
|
||||||
render_feed(conn, params)
|
render_feed(conn, params)
|
||||||
else
|
else
|
||||||
render_error(conn, :not_found, "Not found")
|
render_error(conn, :not_found, "Not found")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_feed(conn, %{"tag" => raw_tag} = params) do
|
defp render_feed(conn, %{"tag" => raw_tag} = params) do
|
||||||
{format, tag} = parse_tag(raw_tag)
|
{format, tag} = parse_tag(raw_tag)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
|
@ -36,12 +36,13 @@ def render_feed(conn, %{"tag" => raw_tag} = params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec parse_tag(binary() | any()) :: {format :: String.t(), tag :: String.t()}
|
@spec parse_tag(binary() | any()) :: {format :: String.t(), tag :: String.t()}
|
||||||
defp parse_tag(raw_tag) when is_binary(raw_tag) do
|
defp parse_tag(raw_tag) do
|
||||||
case Enum.reverse(String.split(raw_tag, ".")) do
|
case is_binary(raw_tag) && Enum.reverse(String.split(raw_tag, ".")) do
|
||||||
[format | tag] when format in ["atom", "rss"] -> {format, Enum.join(tag, ".")}
|
[format | tag] when format in ["rss", "atom"] ->
|
||||||
_ -> {"rss", raw_tag}
|
{format, Enum.join(tag, ".")}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{"atom", raw_tag}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp parse_tag(raw_tag), do: {"rss", raw_tag}
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.Feed.UserController do
|
defmodule Pleroma.Web.Feed.UserController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPubController
|
alias Pleroma.Web.ActivityPub.ActivityPubController
|
||||||
|
@ -22,12 +23,7 @@ def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname
|
||||||
|
|
||||||
def feed_redirect(%{assigns: %{format: format}} = conn, _params)
|
def feed_redirect(%{assigns: %{format: format}} = conn, _params)
|
||||||
when format in ["json", "activity+json"] do
|
when format in ["json", "activity+json"] do
|
||||||
with %{halted: false} = conn <-
|
ActivityPubController.call(conn, :user)
|
||||||
Pleroma.Web.Plugs.EnsureAuthenticatedPlug.call(conn,
|
|
||||||
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
|
|
||||||
) do
|
|
||||||
ActivityPubController.call(conn, :user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def feed_redirect(conn, %{"nickname" => nickname}) do
|
def feed_redirect(conn, %{"nickname" => nickname}) do
|
||||||
|
@ -36,25 +32,18 @@ def feed_redirect(conn, %{"nickname" => nickname}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def feed(conn, params) do
|
def feed(conn, %{"nickname" => nickname} = params) do
|
||||||
unless Pleroma.Config.restrict_unauthenticated_access?(:profiles, :local) do
|
|
||||||
render_feed(conn, params)
|
|
||||||
else
|
|
||||||
errors(conn, {:error, :not_found})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_feed(conn, %{"nickname" => nickname} = params) do
|
|
||||||
format = get_format(conn)
|
format = get_format(conn)
|
||||||
|
|
||||||
format =
|
format =
|
||||||
if format in ["rss", "atom"] do
|
if format in ["atom", "rss"] do
|
||||||
format
|
format
|
||||||
else
|
else
|
||||||
"atom"
|
"atom"
|
||||||
end
|
end
|
||||||
|
|
||||||
with {_, %User{local: true} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
|
with {_, %User{local: true} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)},
|
||||||
|
{_, :visible} <- {:visibility, User.visible_for(user, _reading_user = nil)} do
|
||||||
activities =
|
activities =
|
||||||
%{
|
%{
|
||||||
type: ["Create"],
|
type: ["Create"],
|
||||||
|
@ -69,7 +58,7 @@ def render_feed(conn, %{"nickname" => nickname} = params) do
|
||||||
|> render("user.#{format}",
|
|> render("user.#{format}",
|
||||||
user: user,
|
user: user,
|
||||||
activities: activities,
|
activities: activities,
|
||||||
feed_config: Pleroma.Config.get([:feed])
|
feed_config: Config.get([:feed])
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -81,6 +70,8 @@ def errors(conn, {:error, :not_found}) do
|
||||||
def errors(conn, {:fetch_user, %User{local: false}}), do: errors(conn, {:error, :not_found})
|
def errors(conn, {:fetch_user, %User{local: false}}), do: errors(conn, {:error, :not_found})
|
||||||
def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
|
def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
|
||||||
|
|
||||||
|
def errors(conn, {:visibility, _}), do: errors(conn, {:error, :not_found})
|
||||||
|
|
||||||
def errors(conn, _) do
|
def errors(conn, _) do
|
||||||
render_error(conn, :internal_server_error, "Something went wrong")
|
render_error(conn, :internal_server_error, "Something went wrong")
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,10 +16,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
||||||
alias Pleroma.Web.Plugs.RateLimiter
|
alias Pleroma.Web.Plugs.RateLimiter
|
||||||
alias Pleroma.Web.Router
|
alias Pleroma.Web.Router
|
||||||
|
|
||||||
plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug,
|
|
||||||
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
RateLimiter,
|
RateLimiter,
|
||||||
[name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
|
[name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
|
||||||
|
@ -37,14 +33,12 @@ def object(%{assigns: %{format: format}} = conn, _params)
|
||||||
ActivityPubController.call(conn, :object)
|
ActivityPubController.call(conn, :object)
|
||||||
end
|
end
|
||||||
|
|
||||||
def object(%{assigns: %{format: format}} = conn, _params) do
|
def object(conn, _params) do
|
||||||
with id <- Endpoint.url() <> conn.request_path,
|
with id <- Endpoint.url() <> conn.request_path,
|
||||||
{_, %Activity{} = activity} <-
|
{_, %Activity{} = activity} <-
|
||||||
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
|
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
|
||||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||||
case format do
|
redirect(conn, to: "/notice/#{activity.id}")
|
||||||
_ -> redirect(conn, to: "/notice/#{activity.id}")
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||||
{:error, :not_found}
|
{:error, :not_found}
|
||||||
|
@ -59,13 +53,11 @@ def activity(%{assigns: %{format: format}} = conn, _params)
|
||||||
ActivityPubController.call(conn, :activity)
|
ActivityPubController.call(conn, :activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
def activity(%{assigns: %{format: format}} = conn, _params) do
|
def activity(conn, _params) do
|
||||||
with id <- Endpoint.url() <> conn.request_path,
|
with id <- Endpoint.url() <> conn.request_path,
|
||||||
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
|
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
|
||||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||||
case format do
|
redirect(conn, to: "/notice/#{activity.id}")
|
||||||
_ -> redirect(conn, to: "/notice/#{activity.id}")
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||||
{:error, :not_found}
|
{:error, :not_found}
|
||||||
|
@ -119,6 +111,7 @@ def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do
|
||||||
def notice_player(conn, %{"id" => id}) do
|
def notice_player(conn, %{"id" => id}) do
|
||||||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
|
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
|
||||||
true <- Visibility.is_public?(activity),
|
true <- Visibility.is_public?(activity),
|
||||||
|
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
|
||||||
%Object{} = object <- Object.normalize(activity),
|
%Object{} = object <- Object.normalize(activity),
|
||||||
%{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
|
%{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
|
||||||
true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
|
true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
|
||||||
|
|
|
@ -5,6 +5,26 @@
|
||||||
defmodule Pleroma.Web.Router do
|
defmodule Pleroma.Web.Router do
|
||||||
use Pleroma.Web, :router
|
use Pleroma.Web, :router
|
||||||
|
|
||||||
|
pipeline :accepts_html do
|
||||||
|
plug(:accepts, ["html"])
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :accepts_html_xml do
|
||||||
|
plug(:accepts, ["html", "xml", "rss", "atom"])
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :accepts_html_json do
|
||||||
|
plug(:accepts, ["html", "activity+json", "json"])
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :accepts_html_xml_json do
|
||||||
|
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :accepts_xml_rss_atom do
|
||||||
|
plug(:accepts, ["xml", "rss", "atom"])
|
||||||
|
end
|
||||||
|
|
||||||
pipeline :browser do
|
pipeline :browser do
|
||||||
plug(:accepts, ["html"])
|
plug(:accepts, ["html"])
|
||||||
plug(:fetch_session)
|
plug(:fetch_session)
|
||||||
|
@ -566,30 +586,43 @@ defmodule Pleroma.Web.Router do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :ostatus do
|
|
||||||
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
|
|
||||||
plug(Pleroma.Web.Plugs.StaticFEPlug)
|
|
||||||
end
|
|
||||||
|
|
||||||
pipeline :oembed do
|
|
||||||
plug(:accepts, ["json", "xml"])
|
|
||||||
end
|
|
||||||
|
|
||||||
scope "/", Pleroma.Web do
|
scope "/", Pleroma.Web do
|
||||||
pipe_through([:ostatus, :http_signature])
|
# Note: html format is supported only if static FE is enabled
|
||||||
|
# Note: http signature is only considered for json requests (no auth for non-json requests)
|
||||||
|
pipe_through([:accepts_html_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
|
||||||
|
|
||||||
get("/objects/:uuid", OStatus.OStatusController, :object)
|
get("/objects/:uuid", OStatus.OStatusController, :object)
|
||||||
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
||||||
get("/notice/:id", OStatus.OStatusController, :notice)
|
get("/notice/:id", OStatus.OStatusController, :notice)
|
||||||
get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)
|
|
||||||
|
|
||||||
# Mastodon compatibility routes
|
# Mastodon compatibility routes
|
||||||
get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
|
get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
|
||||||
get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity)
|
get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web do
|
||||||
|
# Note: html format is supported only if static FE is enabled
|
||||||
|
# Note: http signature is only considered for json requests (no auth for non-json requests)
|
||||||
|
pipe_through([:accepts_html_xml_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
|
||||||
|
|
||||||
|
# Note: returns user _profile_ for json requests, redirects to user _feed_ for non-json ones
|
||||||
|
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web do
|
||||||
|
# Note: html format is supported only if static FE is enabled
|
||||||
|
pipe_through([:accepts_html_xml, Pleroma.Web.Plugs.StaticFEPlug])
|
||||||
|
|
||||||
get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
|
get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
|
||||||
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web do
|
||||||
|
pipe_through(:accepts_html)
|
||||||
|
get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web do
|
||||||
|
pipe_through(:accepts_xml_rss_atom)
|
||||||
get("/tags/:tag", Feed.TagController, :feed, as: :tag_feed)
|
get("/tags/:tag", Feed.TagController, :feed, as: :tag_feed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,96 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
||||||
plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)
|
plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)
|
||||||
plug(:assign_id)
|
plug(:assign_id)
|
||||||
|
|
||||||
plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug,
|
|
||||||
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
|
|
||||||
)
|
|
||||||
|
|
||||||
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
|
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
|
||||||
|
|
||||||
|
@doc "Renders requested local public activity or public activities of requested user"
|
||||||
|
def show(%{assigns: %{notice_id: notice_id}} = conn, _params) do
|
||||||
|
with %Activity{local: true} = activity <-
|
||||||
|
Activity.get_by_id_with_object(notice_id),
|
||||||
|
true <- Visibility.is_public?(activity.object),
|
||||||
|
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
|
||||||
|
%User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do
|
||||||
|
meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user})
|
||||||
|
|
||||||
|
timeline =
|
||||||
|
activity.object.data["context"]
|
||||||
|
|> ActivityPub.fetch_activities_for_context(%{})
|
||||||
|
|> Enum.reverse()
|
||||||
|
|> Enum.map(&represent(&1, &1.object.id == activity.object.id))
|
||||||
|
|
||||||
|
render(conn, "conversation.html", %{activities: timeline, meta: meta})
|
||||||
|
else
|
||||||
|
%Activity{object: %Object{data: data}} ->
|
||||||
|
conn
|
||||||
|
|> put_status(:found)
|
||||||
|
|> redirect(external: data["url"] || data["external_url"] || data["id"])
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
not_found(conn, "Post not found.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do
|
||||||
|
with {_, %User{local: true} = user} <-
|
||||||
|
{:fetch_user, User.get_cached_by_nickname_or_id(username_or_id)},
|
||||||
|
{_, :visible} <- {:visibility, User.visible_for(user, _reading_user = nil)} do
|
||||||
|
meta = Metadata.build_tags(%{user: user})
|
||||||
|
|
||||||
|
params =
|
||||||
|
params
|
||||||
|
|> Map.take(@page_keys)
|
||||||
|
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
|
||||||
|
|
||||||
|
timeline =
|
||||||
|
user
|
||||||
|
|> ActivityPub.fetch_user_activities(_reading_user = nil, params)
|
||||||
|
|> Enum.map(&represent/1)
|
||||||
|
|
||||||
|
prev_page_id =
|
||||||
|
(params["min_id"] || params["max_id"]) &&
|
||||||
|
List.first(timeline) && List.first(timeline).id
|
||||||
|
|
||||||
|
next_page_id = List.last(timeline) && List.last(timeline).id
|
||||||
|
|
||||||
|
render(conn, "profile.html", %{
|
||||||
|
user: User.sanitize_html(user),
|
||||||
|
timeline: timeline,
|
||||||
|
prev_page_id: prev_page_id,
|
||||||
|
next_page_id: next_page_id,
|
||||||
|
meta: meta
|
||||||
|
})
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
not_found(conn, "User not found.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(%{assigns: %{object_id: _}} = conn, _params) do
|
||||||
|
url = Helpers.url(conn) <> conn.request_path
|
||||||
|
|
||||||
|
case Activity.get_create_by_object_ap_id_with_object(url) do
|
||||||
|
%Activity{} = activity ->
|
||||||
|
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
||||||
|
redirect(conn, to: to)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
not_found(conn, "Post not found.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(%{assigns: %{activity_id: _}} = conn, _params) do
|
||||||
|
url = Helpers.url(conn) <> conn.request_path
|
||||||
|
|
||||||
|
case Activity.get_by_ap_id(url) do
|
||||||
|
%Activity{} = activity ->
|
||||||
|
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
||||||
|
redirect(conn, to: to)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
not_found(conn, "Post not found.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp get_title(%Object{data: %{"name" => name}}) when is_binary(name),
|
defp get_title(%Object{data: %{"name" => name}}) when is_binary(name),
|
||||||
do: name
|
do: name
|
||||||
|
|
||||||
|
@ -81,91 +165,6 @@ defp represent(%Activity{object: %Object{data: data}} = activity, selected) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def show(%{assigns: %{notice_id: notice_id}} = conn, _params) do
|
|
||||||
with %Activity{local: true} = activity <-
|
|
||||||
Activity.get_by_id_with_object(notice_id),
|
|
||||||
true <- Visibility.is_public?(activity.object),
|
|
||||||
%User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do
|
|
||||||
meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user})
|
|
||||||
|
|
||||||
timeline =
|
|
||||||
activity.object.data["context"]
|
|
||||||
|> ActivityPub.fetch_activities_for_context(%{})
|
|
||||||
|> Enum.reverse()
|
|
||||||
|> Enum.map(&represent(&1, &1.object.id == activity.object.id))
|
|
||||||
|
|
||||||
render(conn, "conversation.html", %{activities: timeline, meta: meta})
|
|
||||||
else
|
|
||||||
%Activity{object: %Object{data: data}} ->
|
|
||||||
conn
|
|
||||||
|> put_status(:found)
|
|
||||||
|> redirect(external: data["url"] || data["external_url"] || data["id"])
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
not_found(conn, "Post not found.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do
|
|
||||||
case User.get_cached_by_nickname_or_id(username_or_id) do
|
|
||||||
%User{} = user ->
|
|
||||||
meta = Metadata.build_tags(%{user: user})
|
|
||||||
|
|
||||||
params =
|
|
||||||
params
|
|
||||||
|> Map.take(@page_keys)
|
|
||||||
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
|
|
||||||
|
|
||||||
timeline =
|
|
||||||
user
|
|
||||||
|> ActivityPub.fetch_user_activities(nil, params)
|
|
||||||
|> Enum.map(&represent/1)
|
|
||||||
|
|
||||||
prev_page_id =
|
|
||||||
(params["min_id"] || params["max_id"]) &&
|
|
||||||
List.first(timeline) && List.first(timeline).id
|
|
||||||
|
|
||||||
next_page_id = List.last(timeline) && List.last(timeline).id
|
|
||||||
|
|
||||||
render(conn, "profile.html", %{
|
|
||||||
user: User.sanitize_html(user),
|
|
||||||
timeline: timeline,
|
|
||||||
prev_page_id: prev_page_id,
|
|
||||||
next_page_id: next_page_id,
|
|
||||||
meta: meta
|
|
||||||
})
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
not_found(conn, "User not found.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def show(%{assigns: %{object_id: _}} = conn, _params) do
|
|
||||||
url = Helpers.url(conn) <> conn.request_path
|
|
||||||
|
|
||||||
case Activity.get_create_by_object_ap_id_with_object(url) do
|
|
||||||
%Activity{} = activity ->
|
|
||||||
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
|
||||||
redirect(conn, to: to)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
not_found(conn, "Post not found.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def show(%{assigns: %{activity_id: _}} = conn, _params) do
|
|
||||||
url = Helpers.url(conn) <> conn.request_path
|
|
||||||
|
|
||||||
case Activity.get_by_ap_id(url) do
|
|
||||||
%Activity{} = activity ->
|
|
||||||
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
|
||||||
redirect(conn, to: to)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
not_found(conn, "Post not found.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
|
defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
|
||||||
do: assign(conn, :notice_id, notice_id)
|
do: assign(conn, :notice_id, notice_id)
|
||||||
|
|
||||||
|
|
|
@ -156,21 +156,6 @@ test "it returns error when user is not found", %{conn: conn} do
|
||||||
|
|
||||||
assert response == "Not found"
|
assert response == "Not found"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{
|
|
||||||
conn: conn
|
|
||||||
} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
put_req_header(
|
|
||||||
conn,
|
|
||||||
"accept",
|
|
||||||
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
|
|
||||||
)
|
|
||||||
|
|
||||||
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}.json", user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "mastodon compatibility routes" do
|
describe "mastodon compatibility routes" do
|
||||||
|
@ -338,18 +323,6 @@ test "cached purged after object deletion", %{conn: conn} do
|
||||||
|
|
||||||
assert "Not found" == json_response(conn2, :not_found)
|
assert "Not found" == json_response(conn2, :not_found)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{
|
|
||||||
conn: conn
|
|
||||||
} do
|
|
||||||
user = insert(:user)
|
|
||||||
note = insert(:note)
|
|
||||||
uuid = String.split(note.data["id"], "/") |> List.last()
|
|
||||||
|
|
||||||
conn = put_req_header(conn, "accept", "application/activity+json")
|
|
||||||
|
|
||||||
ensure_federating_or_authenticated(conn, "/objects/#{uuid}", user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "/activities/:uuid" do
|
describe "/activities/:uuid" do
|
||||||
|
@ -421,18 +394,6 @@ test "cached purged after activity deletion", %{conn: conn} do
|
||||||
|
|
||||||
assert "Not found" == json_response(conn2, :not_found)
|
assert "Not found" == json_response(conn2, :not_found)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{
|
|
||||||
conn: conn
|
|
||||||
} do
|
|
||||||
user = insert(:user)
|
|
||||||
activity = insert(:note_activity)
|
|
||||||
uuid = String.split(activity.data["id"], "/") |> List.last()
|
|
||||||
|
|
||||||
conn = put_req_header(conn, "accept", "application/activity+json")
|
|
||||||
|
|
||||||
ensure_federating_or_authenticated(conn, "/activities/#{uuid}", user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "/inbox" do
|
describe "/inbox" do
|
||||||
|
@ -893,15 +854,6 @@ test "it returns an announce activity in a collection", %{conn: conn} do
|
||||||
|
|
||||||
assert response(conn, 200) =~ announce_activity.data["object"]
|
assert response(conn, 200) =~ announce_activity.data["object"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{
|
|
||||||
conn: conn
|
|
||||||
} do
|
|
||||||
user = insert(:user)
|
|
||||||
conn = put_req_header(conn, "accept", "application/activity+json")
|
|
||||||
|
|
||||||
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}/outbox", user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /users/:nickname/outbox (C2S)" do
|
describe "POST /users/:nickname/outbox (C2S)" do
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
import SweetXml
|
import SweetXml
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Feed.FeedView
|
alias Pleroma.Web.Feed.FeedView
|
||||||
|
@ -15,7 +16,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|
||||||
setup do: clear_config([:feed])
|
setup do: clear_config([:feed])
|
||||||
|
|
||||||
test "gets a feed (ATOM)", %{conn: conn} do
|
test "gets a feed (ATOM)", %{conn: conn} do
|
||||||
Pleroma.Config.put(
|
Config.put(
|
||||||
[:feed, :post_title],
|
[:feed, :post_title],
|
||||||
%{max_length: 25, omission: "..."}
|
%{max_length: 25, omission: "..."}
|
||||||
)
|
)
|
||||||
|
@ -82,7 +83,7 @@ test "gets a feed (ATOM)", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "gets a feed (RSS)", %{conn: conn} do
|
test "gets a feed (RSS)", %{conn: conn} do
|
||||||
Pleroma.Config.put(
|
Config.put(
|
||||||
[:feed, :post_title],
|
[:feed, :post_title],
|
||||||
%{max_length: 25, omission: "..."}
|
%{max_length: 25, omission: "..."}
|
||||||
)
|
)
|
||||||
|
@ -157,7 +158,7 @@ test "gets a feed (RSS)", %{conn: conn} do
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> put_req_header("accept", "application/rss+xml")
|
|> put_req_header("accept", "application/rss+xml")
|
||||||
|> get(tag_feed_path(conn, :feed, "pleromaart"))
|
|> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|
||||||
|> response(200)
|
|> response(200)
|
||||||
|
|
||||||
xml = parse(response)
|
xml = parse(response)
|
||||||
|
@ -183,14 +184,12 @@ test "gets a feed (RSS)", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "private instance" do
|
describe "private instance" do
|
||||||
setup do: clear_config([:instance, :public])
|
setup do: clear_config([:instance, :public], false)
|
||||||
|
|
||||||
test "returns 404 for tags feed", %{conn: conn} do
|
test "returns 404 for tags feed", %{conn: conn} do
|
||||||
Config.put([:instance, :public], false)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_req_header("accept", "application/rss+xml")
|
|> put_req_header("accept", "application/rss+xml")
|
||||||
|> get(tag_feed_path(conn, :feed, "pleromaart"))
|
|> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|
||||||
|> response(404)
|
|> response(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,7 +13,7 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
setup do: clear_config([:instance, :federating], true)
|
setup do: clear_config([:static_fe, :enabled], false)
|
||||||
|
|
||||||
describe "feed" do
|
describe "feed" do
|
||||||
setup do: clear_config([:feed])
|
setup do: clear_config([:feed])
|
||||||
|
@ -192,6 +192,16 @@ test "returns 404 when the user is remote", %{conn: conn} do
|
||||||
|> get(user_feed_path(conn, :feed, user.nickname))
|
|> get(user_feed_path(conn, :feed, user.nickname))
|
||||||
|> response(404)
|
|> response(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not require authentication on non-federating instances", %{conn: conn} do
|
||||||
|
clear_config([:instance, :federating], false)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/rss+xml")
|
||||||
|
|> get("/users/#{user.nickname}/feed.rss")
|
||||||
|
|> response(200)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Note: see ActivityPubControllerTest for JSON format tests
|
# Note: see ActivityPubControllerTest for JSON format tests
|
||||||
|
|
|
@ -7,7 +7,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
alias Pleroma.Config
|
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
@ -21,7 +20,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
setup do: clear_config([:instance, :federating], true)
|
setup do: clear_config([:static_fe, :enabled], false)
|
||||||
|
|
||||||
describe "Mastodon compatibility routes" do
|
describe "Mastodon compatibility routes" do
|
||||||
setup %{conn: conn} do
|
setup %{conn: conn} do
|
||||||
|
@ -215,15 +214,16 @@ test "404s a non-existing notice", %{conn: conn} do
|
||||||
assert response(conn, 404)
|
assert response(conn, 404)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{
|
test "does not require authentication on non-federating instances", %{
|
||||||
conn: conn
|
conn: conn
|
||||||
} do
|
} do
|
||||||
user = insert(:user)
|
clear_config([:instance, :federating], false)
|
||||||
note_activity = insert(:note_activity)
|
note_activity = insert(:note_activity)
|
||||||
|
|
||||||
conn = put_req_header(conn, "accept", "text/html")
|
conn
|
||||||
|
|> put_req_header("accept", "text/html")
|
||||||
ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}", user)
|
|> get("/notice/#{note_activity.id}")
|
||||||
|
|> response(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -325,14 +325,16 @@ test "404s when attachment isn't audio or video", %{conn: conn} do
|
||||||
|> response(404)
|
|> response(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{
|
test "does not require authentication on non-federating instances", %{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
note_activity: note_activity
|
note_activity: note_activity
|
||||||
} do
|
} do
|
||||||
user = insert(:user)
|
clear_config([:instance, :federating], false)
|
||||||
conn = put_req_header(conn, "accept", "text/html")
|
|
||||||
|
|
||||||
ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}/embed_player", user)
|
conn
|
||||||
|
|> put_req_header("accept", "text/html")
|
||||||
|
|> get("/notice/#{note_activity.id}/embed_player")
|
||||||
|
|> response(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,14 +6,12 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config
|
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
setup_all do: clear_config([:static_fe, :enabled], true)
|
setup_all do: clear_config([:static_fe, :enabled], true)
|
||||||
setup do: clear_config([:instance, :federating], true)
|
|
||||||
|
|
||||||
setup %{conn: conn} do
|
setup %{conn: conn} do
|
||||||
conn = put_req_header(conn, "accept", "text/html")
|
conn = put_req_header(conn, "accept", "text/html")
|
||||||
|
@ -74,8 +72,27 @@ test "pagination, page 2", %{conn: conn, user: user} do
|
||||||
refute html =~ ">test29<"
|
refute html =~ ">test29<"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do
|
test "does not require authentication on non-federating instances", %{
|
||||||
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}", user)
|
conn: conn,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
clear_config([:instance, :federating], false)
|
||||||
|
|
||||||
|
conn = get(conn, "/users/#{user.nickname}")
|
||||||
|
|
||||||
|
assert html_response(conn, 200) =~ user.nickname
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 404 for local user with `restrict_unauthenticated/profiles/local` setting", %{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
clear_config([:restrict_unauthenticated, :profiles, :local], true)
|
||||||
|
|
||||||
|
local_user = insert(:user, local: true)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> get("/users/#{local_user.nickname}")
|
||||||
|
|> html_response(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -187,10 +204,28 @@ test "302 for remote cached status", %{conn: conn, user: user} do
|
||||||
assert html_response(conn, 302) =~ "redirected"
|
assert html_response(conn, 302) =~ "redirected"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do
|
test "does not require authentication on non-federating instances", %{
|
||||||
|
conn: conn,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
clear_config([:instance, :federating], false)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"})
|
{:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"})
|
||||||
|
|
||||||
ensure_federating_or_authenticated(conn, "/notice/#{activity.id}", user)
|
conn = get(conn, "/notice/#{activity.id}")
|
||||||
|
|
||||||
|
assert html_response(conn, 200) =~ "testing a thing!"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 404 for local public activity with `restrict_unauthenticated/activities/local` setting",
|
||||||
|
%{conn: conn, user: user} do
|
||||||
|
clear_config([:restrict_unauthenticated, :activities, :local], true)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> get("/notice/#{activity.id}")
|
||||||
|
|> html_response(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -112,28 +112,6 @@ defp json_response_and_validate_schema(
|
||||||
defp json_response_and_validate_schema(conn, _status) do
|
defp json_response_and_validate_schema(conn, _status) do
|
||||||
flunk("Response schema not found for #{conn.method} #{conn.request_path} #{conn.status}")
|
flunk("Response schema not found for #{conn.method} #{conn.request_path} #{conn.status}")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp ensure_federating_or_authenticated(conn, url, user) do
|
|
||||||
initial_setting = Config.get([:instance, :federating])
|
|
||||||
on_exit(fn -> Config.put([:instance, :federating], initial_setting) end)
|
|
||||||
|
|
||||||
Config.put([:instance, :federating], false)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> get(url)
|
|
||||||
|> response(403)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> get(url)
|
|
||||||
|> response(200)
|
|
||||||
|
|
||||||
Config.put([:instance, :federating], true)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> get(url)
|
|
||||||
|> response(200)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue