/
plug.ex
226 lines (184 loc) · 6.51 KB
/
plug.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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
defmodule Pow.Plug do
@moduledoc """
Plug helper methods.
"""
alias Plug.Conn
alias Pow.{Config, Operations}
@private_config_key :pow_config
@doc """
Get the current user assigned to the conn.
The config is fetched from the conn. See `current_user/2` for more.
"""
@spec current_user(Conn.t()) :: map() | nil
def current_user(conn) do
current_user(conn, fetch_config(conn))
end
@doc """
Get the current user assigned to the conn.
This will fetch the user from the assigns map in the conn. The key is by
default `:current_user`, but it can be overridden with
`:current_user_assigns_key` configuration option.
"""
@spec current_user(Conn.t(), Config.t()) :: map() | nil
def current_user(%{assigns: assigns}, config) do
key = current_user_assigns_key(config)
Map.get(assigns, key)
end
@doc """
Assign an authenticated user to the connection.
This will assign the user to the conn. The key is by default `:current_user`,
but it can be overridden with `:current_user_assigns_key` configuration
option.
"""
@spec assign_current_user(Conn.t(), any(), Config.t()) :: Conn.t()
def assign_current_user(conn, user, config) do
key = current_user_assigns_key(config)
Conn.assign(conn, key, user)
end
defp current_user_assigns_key(config) do
Config.get(config, :current_user_assigns_key, :current_user)
end
@doc """
Put the provided config as a private key in the connection.
"""
@spec put_config(Conn.t(), Config.t()) :: Conn.t()
def put_config(conn, config) do
Conn.put_private(conn, @private_config_key, config)
end
@doc """
Fetch configuration from the private key in the connection.
It'll raise an error if configuration hasn't been set as a private key.
"""
@spec fetch_config(Conn.t()) :: Config.t()
def fetch_config(%{private: private}) do
private[@private_config_key] || no_config_error()
end
@doc """
Prepend namespace found in Plug Pow configuration to binary.
Will prepend `:otp_app` if exists in configuration.
"""
@spec prepend_with_namespace(Config.t(), binary()) :: binary()
def prepend_with_namespace(config, string) do
case fetch_namespace(config) do
nil -> string
namespace -> "#{namespace}_#{string}"
end
end
defp fetch_namespace(config), do: Config.get(config, :otp_app)
@doc """
Authenticates a user.
If successful, a new session will be created.
"""
@spec authenticate_user(Conn.t(), map()) :: {:ok | :error, Conn.t()}
def authenticate_user(conn, params) do
config = fetch_config(conn)
params
|> Operations.authenticate(config)
|> case do
nil -> {:error, conn}
user -> {:ok, create(conn, user, config)}
end
end
# TODO: Remove by 1.1.0
@doc false
@deprecated "Use `delete/1` instead"
@spec clear_authenticated_user(Conn.t()) :: {:ok, Conn.t()}
def clear_authenticated_user(conn), do: {:ok, delete(conn)}
@doc """
Creates a changeset from the current authenticated user.
"""
@spec change_user(Conn.t(), map()) :: map()
def change_user(conn, params \\ %{}) do
config = fetch_config(conn)
case current_user(conn, config) do
nil -> Operations.changeset(params, config)
user -> Operations.changeset(user, params, config)
end
end
@doc """
Creates a new user.
If successful, a new session will be created.
"""
@spec create_user(Conn.t(), map()) :: {:ok, map(), Conn.t()} | {:error, map(), Conn.t()}
def create_user(conn, params) do
config = fetch_config(conn)
params
|> Operations.create(config)
|> maybe_create_auth(conn, config)
end
@doc """
Updates the current authenticated user.
If successful, a new session will be created.
"""
@spec update_user(Conn.t(), map()) :: {:ok, map(), Conn.t()} | {:error, map(), Conn.t()}
def update_user(conn, params) do
config = fetch_config(conn)
conn
|> current_user(config)
|> Operations.update(params, config)
|> maybe_create_auth(conn, config)
end
@doc """
Deletes the current authenticated user.
If successful, the user authentication will be cleared from the session.
"""
@spec delete_user(Conn.t()) :: {:ok, map(), Conn.t()} | {:error, map(), Conn.t()}
def delete_user(conn) do
config = fetch_config(conn)
conn
|> current_user(config)
|> Operations.delete(config)
|> case do
{:ok, user} -> {:ok, user, delete(conn, config)}
{:error, changeset} -> {:error, changeset, conn}
end
end
defp maybe_create_auth({:ok, user}, conn, config) do
{:ok, user, create(conn, user, config)}
end
defp maybe_create_auth({:error, changeset}, conn, _config) do
{:error, changeset, conn}
end
# TODO: Remove by 1.1.0
@doc false
@deprecated "Use `get_plug/1` instead"
@spec get_mod(Config.t()) :: atom()
def get_mod(config), do: get_plug(config)
@spec get_plug(Config.t()) :: atom()
def get_plug(config) do
config[:plug] || no_plug_error()
end
@doc """
Call `create/3` for the Pow plug set for the `conn`.
"""
@spec create(Conn.t(), map()) :: Conn.t()
def create(conn, user), do: create(conn, user, fetch_config(conn))
@spec create(Conn.t(), map(), Config.t()) :: Conn.t()
def create(conn, user, config), do: get_plug(config).do_create(conn, user, config)
@doc """
Call `delete/2` for the Pow plug set for the `conn`.
"""
@spec delete(Conn.t()) :: Conn.t()
def delete(conn), do: delete(conn, fetch_config(conn))
@spec delete(Conn.t(), Config.t()) :: Conn.t()
def delete(conn, config), do: get_plug(config).do_delete(conn, config)
@spec no_config_error :: no_return
defp no_config_error do
Config.raise_error("Pow configuration not found in connection. Please use a Pow plug that puts the Pow configuration in the plug connection.")
end
@spec no_plug_error :: no_return
defp no_plug_error do
Config.raise_error("Pow plug was not found in config. Please use a Pow plug that puts the `:plug` in the Pow configuration.")
end
@doc false
@spec __prevent_user_enumeration__(Conn.t(), any()) :: boolean()
def __prevent_user_enumeration__(%{private: %{pow_prevent_user_enumeration: false}}, _changeset), do: false
def __prevent_user_enumeration__(_conn, %{errors: errors}), do: unique_constraint_error?(errors, :email)
def __prevent_user_enumeration__(_conn, _any), do: true
defp unique_constraint_error?(errors, field) do
Enum.find_value(errors, false, fn
{^field, {_msg, [constraint: :unique, constraint_name: _name]}} -> true
_any -> false
end)
end
end