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 %Plug.Conn{halted: false} <- maybe_perform_public_or_authenticated_check(conn),
71 %Plug.Conn{halted: false} <- maybe_perform_authenticated_check(conn),
72 %Plug.Conn{halted: false} <- maybe_halt_on_missing_oauth_scopes_check(conn) do
77 # Ensures instance is public -or- user is authenticated if such check was scheduled
78 defp maybe_perform_public_or_authenticated_check(conn) do
79 if PlugHelper.plug_called?(conn, ExpectPublicOrAuthenticatedCheckPlug) do
80 EnsurePublicOrAuthenticatedPlug.call(conn, %{})
86 # Ensures user is authenticated if such check was scheduled
87 # Note: runs prior to action even if it was already executed earlier in plug chain
88 # (since OAuthScopesPlug has option of proceeding unauthenticated)
89 defp maybe_perform_authenticated_check(conn) do
90 if PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) do
91 EnsureAuthenticatedPlug.call(conn, %{})
97 # Halts if authenticated API action neither performs nor explicitly skips OAuth scopes check
98 defp maybe_halt_on_missing_oauth_scopes_check(conn) do
99 if PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) and
100 not PlugHelper.plug_called_or_skipped?(conn, OAuthScopesPlug) do
104 "Security violation: OAuth scopes check was neither handled nor explicitly skipped."
117 root: "lib/pleroma/web/templates",
118 namespace: Pleroma.Web
120 # Import convenience functions from controllers
121 import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
123 import Pleroma.Web.ErrorHelpers
124 import Pleroma.Web.Gettext
125 import Pleroma.Web.Router.Helpers
129 @doc "Same as `render/3` but wrapped in a rescue block"
130 def safe_render(view, template, assigns \\ %{}) do
131 Phoenix.View.render(view, template, assigns)
135 "#{__MODULE__} failed to render #{inspect({view, template})}\n" <>
136 Exception.format(:error, error, __STACKTRACE__)
143 Same as `render_many/4` but wrapped in rescue block.
145 def safe_render_many(collection, view, template, assigns \\ %{}) do
146 Enum.map(collection, fn resource ->
147 as = Map.get(assigns, :as) || view.__resource__
148 assigns = Map.put(assigns, as, resource)
149 safe_render(view, template, assigns)
159 # credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
161 import Phoenix.Controller
167 # credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
169 import Pleroma.Web.Gettext
175 @behaviour Pleroma.Web.Plug
179 Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain.
181 def skip_plug(conn) do
182 PlugHelper.append_to_private_list(
184 PlugHelper.skipped_plugs_list_id(),
191 If marked as skipped, returns `conn`, otherwise calls `perform/2`.
192 Note: multiple invocations of the same plug (with different or same options) are allowed.
194 def call(%Plug.Conn{} = conn, options) do
195 if PlugHelper.plug_skipped?(conn, __MODULE__) do
199 PlugHelper.append_to_private_list(
201 PlugHelper.called_plugs_list_id(),
205 apply(__MODULE__, :perform, [conn, options])
212 When used, dispatch to the appropriate controller/view/etc.
214 defmacro __using__(which) when is_atom(which) do
215 apply(__MODULE__, which, [])
219 Pleroma.Web.Endpoint.url()