2018-12-23 12:04:54 -08:00
|
|
|
# Pleroma: A lightweight social networking server
|
2021-01-12 22:49:20 -08:00
|
|
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
2018-12-23 12:04:54 -08:00
|
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2017-03-17 09:09:58 -07:00
|
|
|
defmodule Pleroma.Web do
|
|
|
|
@moduledoc """
|
|
|
|
A module that keeps using definitions for controllers,
|
|
|
|
views and so on.
|
|
|
|
|
|
|
|
This can be used in your application as:
|
|
|
|
|
|
|
|
use Pleroma.Web, :controller
|
|
|
|
use Pleroma.Web, :view
|
|
|
|
|
|
|
|
The definitions below will be executed for every view,
|
|
|
|
controller, etc, so keep them short and clean, focused
|
|
|
|
on imports, uses and aliases.
|
|
|
|
|
|
|
|
Do NOT define functions inside the quoted expressions
|
|
|
|
below.
|
|
|
|
"""
|
|
|
|
|
2020-12-06 02:59:10 -08:00
|
|
|
alias Pleroma.Helpers.AuthHelper
|
2020-06-24 00:55:56 -07:00
|
|
|
alias Pleroma.Web.Plugs.EnsureAuthenticatedPlug
|
2020-06-24 00:53:10 -07:00
|
|
|
alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
2020-06-24 00:44:50 -07:00
|
|
|
alias Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug
|
2020-06-24 00:43:19 -07:00
|
|
|
alias Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug
|
2020-06-23 23:57:27 -07:00
|
|
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
2020-06-23 23:42:36 -07:00
|
|
|
alias Pleroma.Web.Plugs.PlugHelper
|
2020-04-21 06:29:19 -07:00
|
|
|
|
2017-03-17 09:09:58 -07:00
|
|
|
def controller do
|
|
|
|
quote do
|
|
|
|
use Phoenix.Controller, namespace: Pleroma.Web
|
2019-07-10 00:52:41 -07:00
|
|
|
|
2017-03-17 09:09:58 -07:00
|
|
|
import Plug.Conn
|
2020-04-21 06:29:19 -07:00
|
|
|
|
2019-02-09 07:20:18 -08:00
|
|
|
import Pleroma.Web.Gettext
|
|
|
|
import Pleroma.Web.Router.Helpers
|
2019-07-10 00:52:41 -07:00
|
|
|
import Pleroma.Web.TranslationHelpers
|
2019-02-21 07:55:19 -08:00
|
|
|
|
2019-02-28 02:00:54 -08:00
|
|
|
plug(:set_put_layout)
|
|
|
|
|
|
|
|
defp set_put_layout(conn, _) do
|
|
|
|
put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
|
|
|
|
end
|
2020-04-06 00:20:44 -07:00
|
|
|
|
2020-04-21 06:29:19 -07:00
|
|
|
# Marks plugs intentionally skipped and blocks their execution if present in plugs chain
|
|
|
|
defp skip_plug(conn, plug_modules) do
|
|
|
|
plug_modules
|
|
|
|
|> List.wrap()
|
|
|
|
|> Enum.reduce(
|
|
|
|
conn,
|
|
|
|
fn plug_module, conn ->
|
|
|
|
try do
|
|
|
|
plug_module.skip_plug(conn)
|
|
|
|
rescue
|
|
|
|
UndefinedFunctionError ->
|
|
|
|
raise "`#{plug_module}` is not skippable. Append `use Pleroma.Web, :plug` to its code."
|
|
|
|
end
|
|
|
|
end
|
|
|
|
)
|
2020-04-06 00:20:44 -07:00
|
|
|
end
|
|
|
|
|
2020-04-17 11:21:10 -07:00
|
|
|
# Executed just before actual controller action, invokes before-action hooks (callbacks)
|
2020-04-06 00:20:44 -07:00
|
|
|
defp action(conn, params) do
|
2021-02-24 13:23:45 -08:00
|
|
|
with %{halted: false} = conn <-
|
|
|
|
maybe_drop_authentication_if_oauth_check_ignored(conn),
|
2020-04-23 11:47:33 -07:00
|
|
|
%{halted: false} = conn <- maybe_perform_public_or_authenticated_check(conn),
|
|
|
|
%{halted: false} = conn <- maybe_perform_authenticated_check(conn),
|
|
|
|
%{halted: false} = conn <- maybe_halt_on_missing_oauth_scopes_check(conn) do
|
2020-04-17 11:21:10 -07:00
|
|
|
super(conn, params)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-22 08:50:25 -07:00
|
|
|
# For non-authenticated API actions, drops auth info if OAuth scopes check was ignored
|
|
|
|
# (neither performed nor explicitly skipped)
|
|
|
|
defp maybe_drop_authentication_if_oauth_check_ignored(conn) do
|
|
|
|
if PlugHelper.plug_called?(conn, ExpectPublicOrAuthenticatedCheckPlug) and
|
|
|
|
not PlugHelper.plug_called_or_skipped?(conn, OAuthScopesPlug) do
|
2020-12-06 02:59:10 -08:00
|
|
|
AuthHelper.drop_auth_info(conn)
|
2020-04-22 08:50:25 -07:00
|
|
|
else
|
|
|
|
conn
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-21 06:29:19 -07:00
|
|
|
# Ensures instance is public -or- user is authenticated if such check was scheduled
|
|
|
|
defp maybe_perform_public_or_authenticated_check(conn) do
|
|
|
|
if PlugHelper.plug_called?(conn, ExpectPublicOrAuthenticatedCheckPlug) do
|
|
|
|
EnsurePublicOrAuthenticatedPlug.call(conn, %{})
|
|
|
|
else
|
|
|
|
conn
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Ensures user is authenticated if such check was scheduled
|
|
|
|
# Note: runs prior to action even if it was already executed earlier in plug chain
|
|
|
|
# (since OAuthScopesPlug has option of proceeding unauthenticated)
|
|
|
|
defp maybe_perform_authenticated_check(conn) do
|
|
|
|
if PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) do
|
|
|
|
EnsureAuthenticatedPlug.call(conn, %{})
|
|
|
|
else
|
|
|
|
conn
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-17 11:21:10 -07:00
|
|
|
# Halts if authenticated API action neither performs nor explicitly skips OAuth scopes check
|
|
|
|
defp maybe_halt_on_missing_oauth_scopes_check(conn) do
|
2020-04-21 06:29:19 -07:00
|
|
|
if PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) and
|
|
|
|
not PlugHelper.plug_called_or_skipped?(conn, OAuthScopesPlug) do
|
2020-04-06 00:20:44 -07:00
|
|
|
conn
|
|
|
|
|> render_error(
|
|
|
|
:forbidden,
|
|
|
|
"Security violation: OAuth scopes check was neither handled nor explicitly skipped."
|
|
|
|
)
|
|
|
|
|> halt()
|
|
|
|
else
|
2020-04-17 11:21:10 -07:00
|
|
|
conn
|
2020-04-06 00:20:44 -07:00
|
|
|
end
|
|
|
|
end
|
2017-03-17 09:09:58 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def view do
|
|
|
|
quote do
|
2018-03-30 06:01:53 -07:00
|
|
|
use Phoenix.View,
|
|
|
|
root: "lib/pleroma/web/templates",
|
|
|
|
namespace: Pleroma.Web
|
2017-03-17 09:09:58 -07:00
|
|
|
|
|
|
|
# Import convenience functions from controllers
|
|
|
|
import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
|
|
|
|
|
2019-02-09 07:20:18 -08:00
|
|
|
import Pleroma.Web.ErrorHelpers
|
|
|
|
import Pleroma.Web.Gettext
|
|
|
|
import Pleroma.Web.Router.Helpers
|
2019-01-27 10:15:35 -08:00
|
|
|
|
|
|
|
require Logger
|
|
|
|
|
|
|
|
@doc "Same as `render/3` but wrapped in a rescue block"
|
|
|
|
def safe_render(view, template, assigns \\ %{}) do
|
|
|
|
Phoenix.View.render(view, template, assigns)
|
|
|
|
rescue
|
|
|
|
error ->
|
|
|
|
Logger.error(
|
2019-08-13 15:15:18 -07:00
|
|
|
"#{__MODULE__} failed to render #{inspect({view, template})}\n" <>
|
|
|
|
Exception.format(:error, error, __STACKTRACE__)
|
2019-01-27 10:15:35 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc """
|
2019-09-10 12:01:45 -07:00
|
|
|
Same as `render_many/4` but wrapped in rescue block.
|
2019-01-27 10:15:35 -08:00
|
|
|
"""
|
2019-09-10 12:01:45 -07:00
|
|
|
def safe_render_many(collection, view, template, assigns \\ %{}) do
|
2019-01-27 10:15:35 -08:00
|
|
|
Enum.map(collection, fn resource ->
|
|
|
|
as = Map.get(assigns, :as) || view.__resource__
|
|
|
|
assigns = Map.put(assigns, as, resource)
|
|
|
|
safe_render(view, template, assigns)
|
|
|
|
end)
|
|
|
|
|> Enum.filter(& &1)
|
|
|
|
end
|
2017-03-17 09:09:58 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def router do
|
|
|
|
quote do
|
|
|
|
use Phoenix.Router
|
2019-02-08 08:18:44 -08:00
|
|
|
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
|
2017-03-17 09:09:58 -07:00
|
|
|
import Plug.Conn
|
|
|
|
import Phoenix.Controller
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def channel do
|
|
|
|
quote do
|
2019-02-08 08:18:44 -08:00
|
|
|
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
|
2020-10-07 11:28:39 -07:00
|
|
|
import Phoenix.Channel
|
2017-03-17 09:09:58 -07:00
|
|
|
import Pleroma.Web.Gettext
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-15 11:19:16 -07:00
|
|
|
def plug do
|
|
|
|
quote do
|
2020-04-21 06:29:19 -07:00
|
|
|
@behaviour Pleroma.Web.Plug
|
|
|
|
@behaviour Plug
|
2020-04-15 11:19:16 -07:00
|
|
|
|
2020-04-17 11:21:10 -07:00
|
|
|
@doc """
|
|
|
|
Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain.
|
|
|
|
"""
|
|
|
|
def skip_plug(conn) do
|
|
|
|
PlugHelper.append_to_private_list(
|
|
|
|
conn,
|
|
|
|
PlugHelper.skipped_plugs_list_id(),
|
|
|
|
__MODULE__
|
|
|
|
)
|
|
|
|
end
|
2020-04-15 11:19:16 -07:00
|
|
|
|
|
|
|
@impl Plug
|
2020-04-21 06:29:19 -07:00
|
|
|
@doc """
|
2020-04-30 08:19:51 -07:00
|
|
|
Before-plug hook that
|
|
|
|
* ensures the plug is not skipped
|
|
|
|
* processes `:if_func` / `:unless_func` functional pre-run conditions
|
|
|
|
* adds plug to the list of called plugs and calls `perform/2` if checks are passed
|
|
|
|
|
2020-04-21 06:29:19 -07:00
|
|
|
Note: multiple invocations of the same plug (with different or same options) are allowed.
|
|
|
|
"""
|
2020-04-15 11:19:16 -07:00
|
|
|
def call(%Plug.Conn{} = conn, options) do
|
2020-04-30 08:19:51 -07:00
|
|
|
if PlugHelper.plug_skipped?(conn, __MODULE__) ||
|
|
|
|
(options[:if_func] && !options[:if_func].(conn)) ||
|
|
|
|
(options[:unless_func] && options[:unless_func].(conn)) do
|
2020-04-15 11:19:16 -07:00
|
|
|
conn
|
|
|
|
else
|
2020-04-21 06:29:19 -07:00
|
|
|
conn =
|
|
|
|
PlugHelper.append_to_private_list(
|
|
|
|
conn,
|
|
|
|
PlugHelper.called_plugs_list_id(),
|
|
|
|
__MODULE__
|
|
|
|
)
|
|
|
|
|
|
|
|
apply(__MODULE__, :perform, [conn, options])
|
2020-04-15 11:19:16 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-03-17 09:09:58 -07:00
|
|
|
@doc """
|
|
|
|
When used, dispatch to the appropriate controller/view/etc.
|
|
|
|
"""
|
|
|
|
defmacro __using__(which) when is_atom(which) do
|
|
|
|
apply(__MODULE__, which, [])
|
|
|
|
end
|
2017-03-30 05:32:36 -07:00
|
|
|
|
|
|
|
def base_url do
|
2018-03-30 06:01:53 -07:00
|
|
|
Pleroma.Web.Endpoint.url()
|
2017-03-30 05:32:36 -07:00
|
|
|
end
|
2021-02-24 13:23:45 -08:00
|
|
|
|
2021-02-25 11:08:36 -08:00
|
|
|
# TODO: Change to Phoenix.Router.routes/1 for Phoenix 1.6.0+
|
2021-02-24 13:23:45 -08:00
|
|
|
def get_api_routes do
|
|
|
|
Pleroma.Web.Router.__routes__()
|
2021-02-25 11:04:08 -08:00
|
|
|
|> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end)
|
2021-02-24 13:23:45 -08:00
|
|
|
|> Enum.map(fn r ->
|
|
|
|
r.path
|
|
|
|
|> String.split("/", trim: true)
|
|
|
|
|> List.first()
|
|
|
|
end)
|
|
|
|
|> Enum.uniq()
|
|
|
|
end
|
2017-03-17 09:09:58 -07:00
|
|
|
end
|