Enforcement of OAuth scopes check for authenticated API endpoints, :skip_plug plug...
[akkoma] / lib / pleroma / web / web.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web do
6 @moduledoc """
7 A module that keeps using definitions for controllers,
8 views and so on.
9
10 This can be used in your application as:
11
12 use Pleroma.Web, :controller
13 use Pleroma.Web, :view
14
15 The definitions below will be executed for every view,
16 controller, etc, so keep them short and clean, focused
17 on imports, uses and aliases.
18
19 Do NOT define functions inside the quoted expressions
20 below.
21 """
22
23 def controller do
24 quote do
25 use Phoenix.Controller, namespace: Pleroma.Web
26
27 import Plug.Conn
28 import Pleroma.Web.Gettext
29 import Pleroma.Web.Router.Helpers
30 import Pleroma.Web.TranslationHelpers
31
32 alias Pleroma.Plugs.PlugHelper
33
34 plug(:set_put_layout)
35
36 defp set_put_layout(conn, _) do
37 put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
38 end
39
40 # Marks a plug as intentionally skipped
41 # (states that the plug is not called for a good reason, not by a mistake)
42 defp skip_plug(conn, plug_module) do
43 PlugHelper.append_to_skipped_plugs(conn, plug_module)
44 end
45
46 # Here we can apply before-action hooks (e.g. verify whether auth checks were preformed)
47 defp action(conn, params) do
48 if conn.private[:auth_expected] &&
49 not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do
50 conn
51 |> render_error(
52 :forbidden,
53 "Security violation: OAuth scopes check was neither handled nor explicitly skipped."
54 )
55 |> halt()
56 else
57 super(conn, params)
58 end
59 end
60 end
61 end
62
63 def view do
64 quote do
65 use Phoenix.View,
66 root: "lib/pleroma/web/templates",
67 namespace: Pleroma.Web
68
69 # Import convenience functions from controllers
70 import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
71
72 import Pleroma.Web.ErrorHelpers
73 import Pleroma.Web.Gettext
74 import Pleroma.Web.Router.Helpers
75
76 require Logger
77
78 @doc "Same as `render/3` but wrapped in a rescue block"
79 def safe_render(view, template, assigns \\ %{}) do
80 Phoenix.View.render(view, template, assigns)
81 rescue
82 error ->
83 Logger.error(
84 "#{__MODULE__} failed to render #{inspect({view, template})}\n" <>
85 Exception.format(:error, error, __STACKTRACE__)
86 )
87
88 nil
89 end
90
91 @doc """
92 Same as `render_many/4` but wrapped in rescue block.
93 """
94 def safe_render_many(collection, view, template, assigns \\ %{}) do
95 Enum.map(collection, fn resource ->
96 as = Map.get(assigns, :as) || view.__resource__
97 assigns = Map.put(assigns, as, resource)
98 safe_render(view, template, assigns)
99 end)
100 |> Enum.filter(& &1)
101 end
102 end
103 end
104
105 def router do
106 quote do
107 use Phoenix.Router
108 # credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
109 import Plug.Conn
110 import Phoenix.Controller
111 end
112 end
113
114 def channel do
115 quote do
116 # credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
117 use Phoenix.Channel
118 import Pleroma.Web.Gettext
119 end
120 end
121
122 @doc """
123 When used, dispatch to the appropriate controller/view/etc.
124 """
125 defmacro __using__(which) when is_atom(which) do
126 apply(__MODULE__, which, [])
127 end
128
129 def base_url do
130 Pleroma.Web.Endpoint.url()
131 end
132 end