X-Git-Url: https://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Fweb.ex;h=4f9281851dd5d43a2f3812d49cd89a7a509ef320;hb=55562ca9362d66553ea3638c91174bbeb6c637f1;hp=bf48ce26c8380202b9208095aed92f9d072abf7a;hpb=019a192e43c2421c74e5126e753aac095db8ad54;p=akkoma diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex index bf48ce26c..4f9281851 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web/web.ex @@ -2,6 +2,11 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only +defmodule Pleroma.Web.Plug do + # Substitute for `call/2` which is defined with `use Pleroma.Web, :plug` + @callback perform(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t() +end + defmodule Pleroma.Web do @moduledoc """ A module that keeps using definitions for controllers, @@ -20,44 +25,91 @@ defmodule Pleroma.Web do below. """ + alias Pleroma.Plugs.EnsureAuthenticatedPlug + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug + alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Plugs.PlugHelper + def controller do quote do use Phoenix.Controller, namespace: Pleroma.Web import Plug.Conn + import Pleroma.Web.Gettext import Pleroma.Web.Router.Helpers import Pleroma.Web.TranslationHelpers - alias Pleroma.Plugs.PlugHelper - plug(:set_put_layout) defp set_put_layout(conn, _) do put_layout(conn, Pleroma.Config.get(:app_layout, "app.html")) end - # Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain - defp skip_plug(conn, plug_module) do - try do - plug_module.skip_plug(conn) - rescue - UndefinedFunctionError -> - raise "#{plug_module} is not skippable. Append `use Pleroma.Web, :plug` to its code." - end + # 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 + ) end # Executed just before actual controller action, invokes before-action hooks (callbacks) defp action(conn, params) do - with %Plug.Conn{halted: false} <- maybe_halt_on_missing_oauth_scopes_check(conn) do + with %{halted: false} = conn <- maybe_drop_authentication_if_oauth_check_ignored(conn), + %{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 super(conn, params) end end + # 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 + OAuthScopesPlug.drop_auth_info(conn) + else + conn + end + end + + # 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 + # Halts if authenticated API action neither performs nor explicitly skips OAuth scopes check defp maybe_halt_on_missing_oauth_scopes_check(conn) do - if Pleroma.Plugs.AuthExpectedPlug.auth_expected?(conn) && - not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do + if PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) and + not PlugHelper.plug_called_or_skipped?(conn, OAuthScopesPlug) do conn |> render_error( :forbidden, @@ -132,7 +184,8 @@ defmodule Pleroma.Web do def plug do quote do - alias Pleroma.Plugs.PlugHelper + @behaviour Pleroma.Web.Plug + @behaviour Plug @doc """ Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain. @@ -146,14 +199,28 @@ defmodule Pleroma.Web do end @impl Plug - @doc "If marked as skipped, returns `conn`, and calls `perform/2` otherwise." + @doc """ + 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 + + Note: multiple invocations of the same plug (with different or same options) are allowed. + """ def call(%Plug.Conn{} = conn, options) do - if PlugHelper.plug_skipped?(conn, __MODULE__) do + if PlugHelper.plug_skipped?(conn, __MODULE__) || + (options[:if_func] && !options[:if_func].(conn)) || + (options[:unless_func] && options[:unless_func].(conn)) do conn else - conn - |> PlugHelper.append_to_private_list(PlugHelper.called_plugs_list_id(), __MODULE__) - |> perform(options) + conn = + PlugHelper.append_to_private_list( + conn, + PlugHelper.called_plugs_list_id(), + __MODULE__ + ) + + apply(__MODULE__, :perform, [conn, options]) end end end