web.ex 4.76 KB
Newer Older
1
# Pleroma: A lightweight social networking server
feld's avatar
feld committed
2
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3
4
# SPDX-License-Identifier: AGPL-3.0-only

lain's avatar
lain committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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.
  """

  def controller do
    quote do
      use Phoenix.Controller, namespace: Pleroma.Web
minibikini's avatar
minibikini committed
26

lain's avatar
lain committed
27
      import Plug.Conn
Haelwenn's avatar
Haelwenn committed
28
29
      import Pleroma.Web.Gettext
      import Pleroma.Web.Router.Helpers
minibikini's avatar
minibikini committed
30
      import Pleroma.Web.TranslationHelpers
Ivan Tashkinov's avatar
Ivan Tashkinov committed
31

32
33
      alias Pleroma.Plugs.PlugHelper

34
35
36
37
38
      plug(:set_put_layout)

      defp set_put_layout(conn, _) do
        put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
      end
39

40
      # Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain
41
      defp skip_plug(conn, plug_module) do
42
        try do
43
          plug_module.skip_plug(conn)
44
45
46
47
        rescue
          UndefinedFunctionError ->
            raise "#{plug_module} is not skippable. Append `use Pleroma.Web, :plug` to its code."
        end
48
49
      end

50
      # Executed just before actual controller action, invokes before-action hooks (callbacks)
51
      defp action(conn, params) do
52
53
54
55
56
57
58
        with %Plug.Conn{halted: false} <- maybe_halt_on_missing_oauth_scopes_check(conn) do
          super(conn, params)
        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
59
        if Pleroma.Plugs.AuthExpectedPlug.auth_expected?(conn) &&
60
61
62
63
64
65
66
67
             not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do
          conn
          |> render_error(
            :forbidden,
            "Security violation: OAuth scopes check was neither handled nor explicitly skipped."
          )
          |> halt()
        else
68
          conn
69
70
        end
      end
lain's avatar
lain committed
71
72
73
74
75
    end
  end

  def view do
    quote do
lain's avatar
lain committed
76
77
78
      use Phoenix.View,
        root: "lib/pleroma/web/templates",
        namespace: Pleroma.Web
lain's avatar
lain committed
79
80
81
82

      # Import convenience functions from controllers
      import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]

Haelwenn's avatar
Haelwenn committed
83
84
85
      import Pleroma.Web.ErrorHelpers
      import Pleroma.Web.Gettext
      import Pleroma.Web.Router.Helpers
86
87
88
89
90
91
92
93
94

      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(
95
96
            "#{__MODULE__} failed to render #{inspect({view, template})}\n" <>
              Exception.format(:error, error, __STACKTRACE__)
97
98
99
100
101
102
          )

          nil
      end

      @doc """
103
      Same as `render_many/4` but wrapped in rescue block.
104
      """
105
      def safe_render_many(collection, view, template, assigns \\ %{}) do
106
107
108
109
110
111
112
        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
lain's avatar
lain committed
113
114
115
116
117
118
    end
  end

  def router do
    quote do
      use Phoenix.Router
Haelwenn's avatar
Stash    
Haelwenn committed
119
      # credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
lain's avatar
lain committed
120
121
122
123
124
125
126
      import Plug.Conn
      import Phoenix.Controller
    end
  end

  def channel do
    quote do
Haelwenn's avatar
Stash    
Haelwenn committed
127
      # credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
lain's avatar
lain committed
128
129
130
131
132
      use Phoenix.Channel
      import Pleroma.Web.Gettext
    end
  end

133
134
135
136
  def plug do
    quote do
      alias Pleroma.Plugs.PlugHelper

137
138
139
140
141
142
143
144
145
146
      @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
147
148
149
150
151
152
153
154

      @impl Plug
      @doc "If marked as skipped, returns `conn`, and calls `perform/2` otherwise."
      def call(%Plug.Conn{} = conn, options) do
        if PlugHelper.plug_skipped?(conn, __MODULE__) do
          conn
        else
          conn
155
          |> PlugHelper.append_to_private_list(PlugHelper.called_plugs_list_id(), __MODULE__)
156
157
158
159
160
161
          |> perform(options)
        end
      end
    end
  end

lain's avatar
lain committed
162
163
164
165
166
167
  @doc """
  When used, dispatch to the appropriate controller/view/etc.
  """
  defmacro __using__(which) when is_atom(which) do
    apply(__MODULE__, which, [])
  end
lain's avatar
lain committed
168
169

  def base_url do
lain's avatar
lain committed
170
    Pleroma.Web.Endpoint.url()
lain's avatar
lain committed
171
  end
lain's avatar
lain committed
172
end