-
Notifications
You must be signed in to change notification settings - Fork 118
/
helpers.ex
236 lines (191 loc) · 6.72 KB
/
helpers.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
227
228
229
230
231
232
233
234
235
236
defmodule Ueberauth.Strategy.Helpers do
@moduledoc """
Provides helper methods for use within your strategy.
These helpers are provided as a convenience for accessing the options passed
to the specific pipelined strategy, considering the pipelined options and
falling back to defaults.
"""
import Plug.Conn, except: [request_url: 1]
alias Ueberauth.Failure
alias Ueberauth.Failure.Error
@doc """
Provides the name of the strategy or provider name.
This is defined in your configuration as the provider name.
"""
@spec strategy_name(Plug.Conn.t()) :: String.t()
def strategy_name(conn), do: from_private(conn, :strategy_name)
@doc """
The strategy module that is being used for the request.
"""
@spec strategy(Plug.Conn.t()) :: module
def strategy(conn), do: from_private(conn, :strategy)
@doc """
The request path for the strategy to hit.
Requests to this path will trigger the `request_phase` of the strategy.
"""
@spec request_path(Plug.Conn.t()) :: String.t()
def request_path(conn), do: from_private(conn, :request_path)
@doc """
The callback path for the requests strategy.
When a client hits this path, the callback phase will be triggered for the strategy.
"""
@spec callback_path(Plug.Conn.t()) :: String.t()
def callback_path(conn), do: from_private(conn, :callback_path)
@doc """
The full url for the request phase for the requests strategy.
The URL is based on the current requests host and scheme. The options will be
encoded as query params.
"""
@spec request_url(Plug.Conn.t()) :: String.t()
def request_url(conn, opts \\ []), do: full_url(conn, request_path(conn), opts)
@doc """
The full URL for the callback phase for the requests strategy.
The URL is based on the current requests host and scheme. The options will be
encoded as query params.
"""
@spec callback_url(Plug.Conn.t()) :: String.t()
def callback_url(conn, opts \\ []) do
from_private(conn, :callback_url) ||
full_url(conn, callback_path(conn), callback_params(conn, opts))
end
@doc """
Build params for callback
This method will filter conn.params with whitelisted params from :callback_params settings
"""
@spec callback_params(Plug.Conn.t()) :: keyword()
def callback_params(conn, opts \\ []) do
callback_params = from_private(conn, :callback_params) || []
callback_params =
callback_params
|> Enum.map(fn k -> {String.to_atom(k), conn.params[k]} end)
|> Enum.filter(fn {_, v} -> v != nil end)
|> Enum.filter(fn {k, _} -> k != "provider" end)
Keyword.merge(opts, callback_params)
end
@doc """
The configured allowed callback http methods.
This will use any supplied options from the configuration, but fallback to the
default options
"""
@spec allowed_callback_methods(Plug.Conn.t()) :: list(String.t())
def allowed_callback_methods(conn), do: from_private(conn, :callback_methods)
@doc """
Is the current request http method one of the allowed callback methods?
"""
@spec allowed_callback_method?(Plug.Conn.t()) :: boolean
def allowed_callback_method?(%{method: method} = conn) do
callback_method =
method
|> to_string
|> String.upcase()
conn
|> allowed_callback_methods
|> Enum.member?(callback_method)
end
@doc """
The full list of options passed to the strategy in the configuration.
"""
@spec options(Plug.Conn.t()) :: Keyword.t()
def options(conn), do: from_private(conn, :options)
@doc """
A helper for constructing error entries on failure.
The `message_key` is intended for use by machines for translations etc.
The message is a human readable error message.
#### Example
error("something_bad", "Something really bad happened")
"""
@spec error(String.t(), String.t()) :: Error.t()
def error(key, message), do: struct(Error, message_key: key, message: message)
@doc """
Sets a failure onto the connection containing a List of errors.
During your callback phase, this should be called to 'fail' the authentication
request and include a collection of errors outlining what the problem is.
Note this changes the conn object and should be part of your returned
connection of the `callback_phase!`.
"""
@spec error(Plug.Conn.t(), list(Error.t())) :: Plug.Conn.t()
def set_errors!(conn, errors) do
failure =
struct(
Failure,
provider: strategy_name(conn),
strategy: strategy(conn),
errors: map_errors(errors)
)
Plug.Conn.assign(conn, :ueberauth_failure, failure)
end
@doc """
Redirects to a url and halts the plug pipeline.
"""
@spec redirect!(Plug.Conn.t(), String.t()) :: Plug.Conn.t()
def redirect!(conn, url) do
html = Plug.HTML.html_escape(url)
body = "<html><body>You are being <a href=\"#{html}\">redirected</a>.</body></html>"
conn
|> put_resp_header("location", url)
|> send_resp(conn.status || 302, body)
|> halt
end
@doc """
Add state parameter to the `%Plug.Conn{}`.
"""
@spec add_state_param(Plug.Conn.t(), String.t()) :: Plug.Conn.t()
def add_state_param(conn, value) do
Plug.Conn.put_private(conn, :ueberauth_state_param, value)
end
@doc """
Add state parameter to the options.
"""
@spec with_state_param(
keyword(),
Plug.Conn.t()
) :: keyword()
def with_state_param(opts, conn) do
state = conn.private[:ueberauth_state_param]
if is_nil(state) do
opts
else
Keyword.put(opts, :state, state)
end
end
defp from_private(conn, key) do
opts = conn.private[:ueberauth_request_options]
if opts, do: opts[key], else: nil
end
defp full_url(conn, path, opts) do
scheme =
conn
|> forwarded_proto
|> coalesce(conn.scheme)
|> normalize_scheme
%URI{
host: conn.host,
port: normalize_port(scheme, conn.port),
path: path,
query: encode_query(opts),
scheme: to_string(scheme)
}
|> to_string
end
defp forwarded_proto(conn) do
conn
|> Plug.Conn.get_req_header("x-forwarded-proto")
|> List.first()
end
defp normalize_scheme("https"), do: :https
defp normalize_scheme("http"), do: :http
defp normalize_scheme(scheme), do: scheme
defp coalesce(nil, second), do: second
defp coalesce(first, _), do: first
defp normalize_port(:https, 80), do: 443
defp normalize_port(_, port), do: port
defp encode_query([]), do: nil
defp encode_query(opts), do: URI.encode_query(opts)
defp map_errors(nil), do: []
defp map_errors([]), do: []
defp map_errors(%Error{} = error), do: [error]
defp map_errors(errors), do: Enum.map(errors, &p_error/1)
defp p_error(%Error{} = error), do: error
defp p_error(%{} = error), do: struct(Error, error)
defp p_error(error) when is_list(error), do: struct(Error, error)
end