1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.Plug do
6 # Substitute for `call/2` which is defined with `use Pleroma.Web, :plug`
7 @callback perform(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
10 defmodule Pleroma.Web do
12 A module that keeps using definitions for controllers,
15 This can be used in your application as:
17 use Pleroma.Web, :controller
18 use Pleroma.Web, :view
20 The definitions below will be executed for every view,
21 controller, etc, so keep them short and clean, focused
22 on imports, uses and aliases.
24 Do NOT define functions inside the quoted expressions
28 alias Pleroma.Plugs.EnsureAuthenticatedPlug
29 alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
30 alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug
31 alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug
32 alias Pleroma.Plugs.OAuthScopesPlug
33 alias Pleroma.Plugs.PlugHelper
37 use Phoenix.Controller, namespace: Pleroma.Web
41 import Pleroma.Web.Gettext
42 import Pleroma.Web.Router.Helpers
43 import Pleroma.Web.TranslationHelpers
47 defp set_put_layout(conn, _) do
48 put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
51 # Marks plugs intentionally skipped and blocks their execution if present in plugs chain
52 defp skip_plug(conn, plug_modules) do
57 fn plug_module, conn ->
59 plug_module.skip_plug(conn)
61 UndefinedFunctionError ->
62 raise "`#{plug_module}` is not skippable. Append `use Pleroma.Web, :plug` to its code."
68 # Executed just before actual controller action, invokes before-action hooks (callbacks)
69 defp action(conn, params) do
70 with %{halted: false} = conn <- maybe_drop_authentication_if_oauth_check_ignored(conn),
71 %{halted: false} = conn <- maybe_perform_public_or_authenticated_check(conn),
72 %{halted: false} = conn <- maybe_perform_authenticated_check(conn),
73 %{halted: false} = conn <- maybe_halt_on_missing_oauth_scopes_check(conn) do
78 # For non-authenticated API actions, drops auth info if OAuth scopes check was ignored
79 # (neither performed nor explicitly skipped)
80 defp maybe_drop_authentication_if_oauth_check_ignored(conn) do
81 if PlugHelper.plug_called?(conn, ExpectPublicOrAuthenticatedCheckPlug) and
82 not PlugHelper.plug_called_or_skipped?(conn, OAuthScopesPlug) do
83 OAuthScopesPlug.drop_auth_info(conn)
89 # Ensures instance is public -or- user is authenticated if such check was scheduled
90 defp maybe_perform_public_or_authenticated_check(conn) do
91 if PlugHelper.plug_called?(conn, ExpectPublicOrAuthenticatedCheckPlug) do
92 EnsurePublicOrAuthenticatedPlug.call(conn, %{})
98 # Ensures user is authenticated if such check was scheduled
99 # Note: runs prior to action even if it was already executed earlier in plug chain
100 # (since OAuthScopesPlug has option of proceeding unauthenticated)
101 defp maybe_perform_authenticated_check(conn) do
102 if PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) do
103 EnsureAuthenticatedPlug.call(conn, %{})
109 # Halts if authenticated API action neither performs nor explicitly skips OAuth scopes check
110 defp maybe_halt_on_missing_oauth_scopes_check(conn) do
111 if PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) and
112 not PlugHelper.plug_called_or_skipped?(conn, OAuthScopesPlug) do
116 "Security violation: OAuth scopes check was neither handled nor explicitly skipped."
129 root: "lib/pleroma/web/templates",
130 namespace: Pleroma.Web
132 # Import convenience functions from controllers
133 import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
135 import Pleroma.Web.ErrorHelpers
136 import Pleroma.Web.Gettext
137 import Pleroma.Web.Router.Helpers
141 @doc "Same as `render/3` but wrapped in a rescue block"
142 def safe_render(view, template, assigns \\ %{}) do
143 Phoenix.View.render(view, template, assigns)
147 "#{__MODULE__} failed to render #{inspect({view, template})}\n" <>
148 Exception.format(:error, error, __STACKTRACE__)
155 Same as `render_many/4` but wrapped in rescue block.
157 def safe_render_many(collection, view, template, assigns \\ %{}) do
158 Enum.map(collection, fn resource ->
159 as = Map.get(assigns, :as) || view.__resource__
160 assigns = Map.put(assigns, as, resource)
161 safe_render(view, template, assigns)
171 # credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
173 import Phoenix.Controller
179 # credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
181 import Pleroma.Web.Gettext
187 @behaviour Pleroma.Web.Plug
191 Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain.
193 def skip_plug(conn) do
194 PlugHelper.append_to_private_list(
196 PlugHelper.skipped_plugs_list_id(),
203 If marked as skipped, returns `conn`, otherwise calls `perform/2`.
204 Note: multiple invocations of the same plug (with different or same options) are allowed.
206 def call(%Plug.Conn{} = conn, options) do
207 if PlugHelper.plug_skipped?(conn, __MODULE__) do
211 PlugHelper.append_to_private_list(
213 PlugHelper.called_plugs_list_id(),
217 apply(__MODULE__, :perform, [conn, options])
224 When used, dispatch to the appropriate controller/view/etc.
226 defmacro __using__(which) when is_atom(which) do
227 apply(__MODULE__, which, [])
231 Pleroma.Web.Endpoint.url()