/
controller.ex
171 lines (137 loc) · 5.32 KB
/
controller.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
defmodule Pow.Phoenix.Controller do
@moduledoc """
Used with Pow Phoenix controllers to handle messages, routes and callbacks.
## Usage
defmodule MyPowExtension.Phoenix.MyController do
use Pow.Phoenix.Controller
def process_new(conn, _params) do
{:ok, :response, conn}
end
def respond_new({:ok, :response, conn}) do
render(conn, "new.html")
end
end
## Configuration options
* `:messages_backend` - See `Pow.Phoenix.Messages` for more.
* `:routes_backend` - See `Pow.Phoenix.Routes` for more.
* `:controller_callbacks` - See
`Pow.Extension.Phoenix.ControllerCallbacks` for more.
"""
alias Plug.Conn
alias Pow.Config
alias Pow.Phoenix.{Messages, PlugErrorHandler, Routes, ViewHelpers}
alias Pow.Plug
@doc false
defmacro __using__(config) do
quote do
use Phoenix.Controller,
namespace: Pow.Phoenix
import unquote(__MODULE__), only: [require_authenticated: 2, require_not_authenticated: 2, put_no_cache_header: 2]
plug :pow_layout, unquote(config)
def action(conn, _opts), do: unquote(__MODULE__).action(__MODULE__, conn, conn.params)
defp pow_layout(conn, _config), do: ViewHelpers.layout(conn)
unquote(__MODULE__).__define_helper_methods__()
end
end
@doc false
defmacro __define_helper_methods__ do
quote do
def messages(conn), do: unquote(__MODULE__).messages(conn, Messages)
def routes(conn), do: unquote(__MODULE__).routes(conn, Routes)
defoverridable messages: 1, routes: 1
end
end
@doc """
Handles the controller action call.
If a `:controller_callbacks` module has been set in the configuration,
then `before_process` and `before_respond` will be called on this module
on all actions.
"""
@spec action(atom(), Conn.t(), map()) :: Conn.t()
def action(controller, %{private: private} = conn, params) do
action = private.phoenix_action
config = Plug.fetch_config(conn)
callbacks = Config.get(config, :controller_callbacks)
conn
|> maybe_callback(callbacks, :before_process, controller, action, config)
|> process_action(controller, action, params)
|> maybe_callback(callbacks, :before_respond, controller, action, config)
|> respond_action(controller, action)
end
defp process_action({:halt, conn}, _controller, _action, _params), do: {:halt, conn}
defp process_action(conn, controller, action, params) do
apply(controller, String.to_atom("process_#{action}"), [conn, params])
end
defp respond_action({:halt, conn}, _controller, _action), do: conn
defp respond_action(results, controller, action) do
apply(controller, String.to_atom("respond_#{action}"), [results])
end
defp maybe_callback({:halt, conn}, _callbacks, _hook, _controller, _action, _config),
do: {:halt, conn}
defp maybe_callback(results, nil, _hook, _controller, _action, _config), do: results
defp maybe_callback(results, callbacks, hook, controller, action, config) do
apply(callbacks, hook, [controller, action, results, config])
end
@doc """
Fetches messages backend from configuration, or use fallback.
"""
@spec messages(Conn.t(), atom()) :: atom()
def messages(conn, fallback) do
conn
|> Plug.fetch_config()
|> Config.get(:messages_backend, fallback)
end
@doc """
Fetches routes backend from configuration, or use fallback.
"""
@spec routes(Conn.t(), atom()) :: atom()
def routes(conn, fallback) do
conn
|> Plug.fetch_config()
|> Config.get(:routes_backend, fallback)
end
@spec route_helper(atom()) :: binary()
def route_helper(plug) do
as = Phoenix.Naming.resource_name(plug, "Controller")
[base | _rest] = Module.split(plug)
base = Macro.underscore(base)
"#{base}_#{as}"
end
@doc """
Ensures that user has been authenticated.
`Pow.Phoenix.PlugErrorHandler` is used as error handler. See
`Pow.Plug.RequireAuthenticated` for more.
"""
@spec require_authenticated(Conn.t(), Keyword.t()) :: Conn.t()
def require_authenticated(conn, _opts) do
opts = Plug.RequireAuthenticated.init(error_handler: PlugErrorHandler)
Plug.RequireAuthenticated.call(conn, opts)
end
@doc """
Ensures that user hasn't been authenticated.
`Pow.Phoenix.PlugErrorHandler` is used as error handler. See
`Pow.Plug.RequireNotAuthenticated` for more.
"""
@spec require_not_authenticated(Conn.t(), Keyword.t()) :: Conn.t()
def require_not_authenticated(conn, _opts) do
opts = Plug.RequireNotAuthenticated.init(error_handler: PlugErrorHandler)
Plug.RequireNotAuthenticated.call(conn, opts)
end
@default_cache_control_header Conn.get_resp_header(struct(Conn), "cache-control")
@no_cache_control_header "no-cache, no-store, must-revalidate"
@doc """
Ensures that the page can't be cached in browser.
This will add a "cache-control" header with
"no-cache, no-store, must-revalidate" if the cache control header hasn't
been changed from the default value in the `Plug.Conn` struct.
"""
@spec put_no_cache_header(Conn.t(), Keyword.t()) :: Conn.t()
def put_no_cache_header(conn, _opts) do
conn
|> Conn.get_resp_header("cache-control")
|> case do
@default_cache_control_header -> Conn.put_resp_header(conn, "cache-control", @no_cache_control_header)
_any -> conn
end
end
end