Go to line 1
defmodule HexpmWeb.ControllerHelpers do
Go to line 2
import Plug.Conn
Go to line 3
import Phoenix.Controller
Go to line 5
alias Hexpm.Accounts.{Auth, Organizations}
Go to line 6
alias Hexpm.Repository.{Packages, Releases, Repositories}
Go to line 7
alias HexpmWeb.Router.Helpers, as: Routes
Go to line 9
@max_cache_age 60
Go to line 11
# TODO: check privacy settings
TODO found
Go to line 12
def cache(conn, control, vary) do
Go to line 13
conn
Go to line 14
|> maybe_put_resp_header("cache-control", parse_control(control))
Go to line 15
|> maybe_put_resp_header("vary", parse_vary(vary))
Go to line 16
end
Go to line 18
def api_cache(conn, privacy) do
Go to line 19
control = [logged_in_privacy(conn, privacy), "max-age": @max_cache_age]
Go to line 20
vary = ["accept", "accept-encoding"]
Go to line 21
cache(conn, control, vary)
Go to line 22
end
Go to line 24
defp logged_in_privacy(conn, :logged_in) do
Go to line 25
if conn.assigns.current_user, do: :private, else: :public
Go to line 26
end
Go to line 28
defp logged_in_privacy(_conn, other) do
Go to line 29
other
Go to line 30
end
Go to line 32
defp parse_vary(nil), do: nil
Go to line 33
defp parse_vary(vary), do: Enum.map_join(vary, ", ", &"#{&1}")
Go to line 35
defp parse_control(nil), do: nil
Go to line 37
defp parse_control(control) do
Go to line 38
Enum.map_join(control, ", ", fn
Go to line 39
atom when is_atom(atom) -> "#{atom}"
Go to line 40
{key, value} -> "#{key}=#{value}"
Go to line 41
end)
Go to line 42
end
Go to line 44
defp maybe_put_resp_header(conn, _header, nil), do: conn
Go to line 45
defp maybe_put_resp_header(conn, header, value), do: put_resp_header(conn, header, value)
Go to line 47
def render_error(conn, status, assigns \\ []) do
Go to line 48
conn
Go to line 49
|> put_status(status)
Go to line 50
|> put_layout(false)
Go to line 51
|> put_view(HexpmWeb.ErrorView)
Go to line 52
|> render(:"#{status}", assigns)
Go to line 53
|> halt()
Go to line 54
end
Go to line 56
def validation_failed(conn, %Ecto.Changeset{} = changeset) do
Go to line 57
errors = translate_errors(changeset)
Go to line 58
render_error(conn, 422, errors: errors)
Go to line 59
end
Go to line 61
def validation_failed(conn, errors) do
Go to line 62
render_error(conn, 422, errors: errors)
Go to line 63
end
Go to line 65
def translate_errors(changeset) do
Go to line 66
Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
Go to line 67
case {message, Keyword.fetch(opts, :type)} do
Go to line 68
{"is invalid", {:ok, type}} -> type_error(type)
Go to line 69
_ -> interpolate_errors(message, opts)
Go to line 70
end
Go to line 71
end)
Go to line 72
|> normalize_errors()
Go to line 73
end
Go to line 75
defp interpolate_errors(message, opts) do
Go to line 76
Enum.reduce(opts, message, fn {key, value}, message ->
Go to line 77
pattern = "%{#{key}}"
Go to line 79
if String.contains?(message, pattern) do
Go to line 80
if String.Chars.impl_for(value) do
Go to line 81
String.replace(message, pattern, to_string(value))
Go to line 82
else
Go to line 83
raise "Unable to translate error: #{inspect({message, opts})}"
Go to line 84
end
Go to line 85
else
Go to line 86
message
Go to line 87
end
Go to line 88
end)
Go to line 89
end
Go to line 91
defp type_error(type), do: "expected type #{pretty_type(type)}"
Go to line 93
defp pretty_type({:array, type}), do: "list(#{pretty_type(type)})"
Go to line 94
defp pretty_type({:map, type}), do: "map(#{pretty_type(type)})"
Go to line 95
defp pretty_type(type), do: type |> inspect() |> String.trim_leading(":")
Go to line 97
# Since Changeset.traverse_errors returns `{field: [err], ...}`
Go to line 98
# but Hex client expects `{field: err1, ...}` we normalize to the latter.
Go to line 99
defp normalize_errors(errors) do
Go to line 100
Enum.flat_map(errors, &normalize_key_value/1)
Go to line 101
|> Map.new()
Go to line 102
end
Go to line 104
defp normalize_key_value({key, value}) do
Go to line 105
case value do
Go to line 106
_ when value == %{} ->
Go to line 109
[%{} | _] = value ->
Go to line 110
value = Enum.reduce(value, %{}, &Map.merge(&2, normalize_errors(&1)))
Go to line 111
[{key, value}]
Go to line 113
[] ->
Go to line 116
value when is_map(value) ->
Go to line 117
[{key, normalize_errors(value)}]
Go to line 119
[value | _] ->
Go to line 120
[{key, value}]
Go to line 121
end
Go to line 122
end
Go to line 124
def not_found(conn) do
Go to line 125
render_error(conn, 404)
Go to line 126
end
Go to line 128
def when_stale(conn, entities, opts \\ [], fun) do
Go to line 129
etag = etag(entities)
Go to line 130
modified = if Keyword.get(opts, :modified, true), do: last_modified(entities)
Go to line 132
conn =
Go to line 133
conn
Go to line 134
|> put_etag(etag)
Go to line 135
|> put_last_modified(modified)
Go to line 137
if fresh?(conn, etag: etag, modified: modified) do
Go to line 138
send_resp(conn, 304, "")
Go to line 139
else
Go to line 140
fun.(conn)
Go to line 141
end
Go to line 142
end
Go to line 144
defp put_etag(conn, nil) do
Go to line 145
conn
Go to line 146
end
Go to line 148
defp put_etag(conn, etag) do
Go to line 149
put_resp_header(conn, "etag", etag)
Go to line 150
end
Go to line 152
defp put_last_modified(conn, nil) do
Go to line 153
conn
Go to line 154
end
Go to line 156
defp put_last_modified(conn, modified) do
Go to line 157
put_resp_header(conn, "last-modified", :cowboy_clock.rfc1123(modified))
Go to line 158
end
Go to line 160
defp fresh?(conn, opts) do
Go to line 161
not expired?(conn, opts)
Go to line 162
end
Go to line 164
defp expired?(conn, opts) do
Go to line 165
modified_since = List.first(get_req_header(conn, "if-modified-since"))
Go to line 166
none_match = List.first(get_req_header(conn, "if-none-match"))
Go to line 168
if modified_since || none_match do
Go to line 169
modified_since?(modified_since, opts[:modified]) or none_match?(none_match, opts[:etag])
Go to line 170
else
Go to line 171
true
Go to line 172
end
Go to line 173
end
Go to line 175
defp modified_since?(header, last_modified) do
Go to line 176
if header && last_modified do
Go to line 177
modified_since = :httpd_util.convert_request_date(String.to_charlist(header))
Go to line 178
modified_since = :calendar.datetime_to_gregorian_seconds(modified_since)
Go to line 179
last_modified = :calendar.datetime_to_gregorian_seconds(last_modified)
Go to line 180
last_modified > modified_since
Go to line 181
else
Go to line 182
false
Go to line 183
end
Go to line 184
end
Go to line 186
defp none_match?(none_match, etag) do
Go to line 187
if none_match && etag do
Go to line 188
none_match = Plug.Conn.Utils.list(none_match)
Go to line 189
etag not in none_match and "*" not in none_match
Go to line 190
else
Go to line 191
false
Go to line 192
end
Go to line 193
end
Go to line 195
defp etag(schemas) do
Go to line 196
binary =
Go to line 197
schemas
Go to line 198
|> List.wrap()
Go to line 199
|> Enum.map(&HexpmWeb.Stale.etag/1)
Go to line 200
|> List.flatten()
Go to line 201
|> :erlang.term_to_binary()
Go to line 203
:crypto.hash(:md5, binary)
Go to line 204
|> Base.encode16(case: :lower)
Go to line 205
end
Go to line 207
def last_modified(schemas) do
Go to line 208
schemas
Go to line 209
|> List.wrap()
Go to line 210
|> Enum.map(&HexpmWeb.Stale.last_modified/1)
Go to line 211
|> List.flatten()
Go to line 212
|> Enum.reject(&is_nil/1)
Go to line 213
|> Enum.map(&time_to_erl/1)
Go to line 214
|> Enum.max()
Go to line 215
end
Go to line 217
defp time_to_erl(%NaiveDateTime{} = datetime), do: NaiveDateTime.to_erl(datetime)
Go to line 218
defp time_to_erl(%DateTime{} = datetime), do: NaiveDateTime.to_erl(datetime)
Go to line 219
defp time_to_erl(%Date{} = date), do: {Date.to_erl(date), {0, 0, 0}}
Go to line 221
def fetch_repository(conn, _opts) do
Go to line 222
if param = conn.params["repository"] do
Go to line 223
if repository = Repositories.get(param, [:organization]) do
Go to line 224
conn
Go to line 225
|> assign(:repository, repository)
Go to line 226
|> assign(:organization, repository.organization)
Go to line 227
else
Go to line 228
conn
Go to line 229
|> HexpmWeb.AuthHelpers.forbidden("account not authorized for this action")
Go to line 230
|> halt()
Go to line 231
end
Go to line 232
else
Go to line 233
conn
Go to line 234
|> assign(:repository, nil)
Go to line 235
|> assign(:organization, nil)
Go to line 236
end
Go to line 237
end
Go to line 239
def fetch_organization(conn, _opts) do
Go to line 240
if param = conn.params["organization"] do
Go to line 241
if organization = Organizations.get(param) do
Go to line 242
assign(conn, :organization, organization)
Go to line 243
else
Go to line 244
conn
Go to line 245
|> HexpmWeb.AuthHelpers.forbidden("account not authorized for this action")
Go to line 246
|> halt()
Go to line 247
end
Go to line 248
else
Go to line 249
assign(conn, :organization, nil)
Go to line 250
end
Go to line 251
end
Go to line 253
def maybe_fetch_package(conn, _opts) do
Go to line 254
repository = Repositories.get(conn.params["repository"], [:organization])
Go to line 255
package = repository && Packages.get(repository, conn.params["name"])
Go to line 257
conn
Go to line 258
|> assign(:repository, repository)
Go to line 259
|> assign(:package, package)
Go to line 260
|> assign(:organization, repository && repository.organization)
Go to line 261
end
Go to line 263
def fetch_release(conn, _opts) do
Go to line 264
case Version.parse(conn.params["version"]) do
Go to line 265
{:ok, version} ->
Go to line 266
repository = Repositories.get(conn.params["repository"], [:organization])
Go to line 267
package = repository && Packages.get(repository, conn.params["name"])
Go to line 268
release = package && Releases.get(package, version)
Go to line 270
if release do
Go to line 271
conn
Go to line 272
|> assign(:repository, repository)
Go to line 273
|> assign(:package, package)
Go to line 274
|> assign(:release, release)
Go to line 275
|> assign(:organization, repository && repository.organization)
Go to line 276
else
Go to line 277
conn
Go to line 278
|> not_found()
Go to line 279
|> halt()
Go to line 280
end
Go to line 282
:error ->
Go to line 283
render_error(conn, 400, message: "invalid version: #{conn.params["version"]}")
Go to line 284
end
Go to line 285
end
Go to line 287
def maybe_fetch_release(conn, _opts) do
Go to line 288
case Version.parse(conn.params["version"]) do
Go to line 289
{:ok, version} ->
Go to line 290
repository = Repositories.get(conn.params["repository"], [:organization])
Go to line 291
package = repository && Packages.get(repository, conn.params["name"])
Go to line 292
release = package && Releases.get(package, version)
Go to line 294
conn
Go to line 295
|> assign(:repository, repository)
Go to line 296
|> assign(:package, package)
Go to line 297
|> assign(:release, release)
Go to line 298
|> assign(:organization, repository && repository.organization)
Go to line 300
:error ->
Go to line 301
render_error(conn, 400, message: "invalid version: #{conn.params["version"]}")
Go to line 302
end
Go to line 303
end
Go to line 305
def required_params(conn, required_param_names) do
Go to line 306
remaining = required_param_names -- Map.keys(conn.params)
Go to line 308
if remaining == [] do
Go to line 309
conn
Go to line 310
else
Go to line 311
names = Enum.map_join(remaining, ", ", &inspect/1)
Go to line 312
message = "missing required parameters: #{names}"
Go to line 313
render_error(conn, 400, message: message)
Go to line 314
end
Go to line 315
end
Go to line 317
def authorize(conn, opts) do
Go to line 318
HexpmWeb.AuthHelpers.authorized(conn, opts)
Go to line 319
end
Go to line 321
def maybe_authorize(conn, opts) do
Go to line 322
HexpmWeb.AuthHelpers.maybe_authorized(conn, opts)
Go to line 323
end
Go to line 325
def audit_data(conn) do
Go to line 326
user_or_organization = conn.assigns.current_user || conn.assigns.current_organization
Go to line 327
{user_or_organization, conn.assigns.user_agent}
Go to line 328
end
Go to line 330
def password_auth(username, password) do
Go to line 331
case Auth.password_auth(username, password) do
Go to line 332
{:ok, %{user: user, email: email}} ->
Go to line 333
if email.verified,
Go to line 334
do: {:ok, user},
Go to line 335
else: {:error, :unconfirmed}
Go to line 337
:error ->
Go to line 338
{:error, :wrong}
Go to line 339
end
Go to line 340
end
Go to line 342
def auth_error_message(:wrong), do: "Invalid username, email or password."
Go to line 344
def auth_error_message(:unconfirmed),
Go to line 345
do: "Email has not been verified yet. You can resend the verification email below."
Go to line 347
def password_breached_message(conn, _opts) do
Go to line 348
# docs_path + anchor #password-security
Go to line 349
"The password you provided has previously been breached. " <>
Go to line 350
"To increase your security, please change your password." <>
Go to line 351
"<br /><a class=\"small\" href=\"#{Routes.docs_path(conn, :faq)}#password-security\">" <>
Go to line 352
"Learn more about our password security.</a>"
Go to line 353
end
Go to line 355
def requires_login(conn, _opts) do
Go to line 356
if logged_in?(conn) do
Go to line 357
conn
Go to line 358
else
Go to line 359
redirect(conn, to: Routes.login_path(conn, :show, return: conn.request_path))
Go to line 360
|> halt
Go to line 361
end
Go to line 362
end
Go to line 364
def logged_in?(conn) do
Go to line 365
!!conn.assigns[:current_user]
Go to line 366
end
Go to line 368
def nillify_params(conn, keys) do
Go to line 369
params =
Go to line 370
Enum.reduce(keys, conn.params, fn key, params ->
Go to line 371
case Map.fetch(conn.params, key) do
Go to line 372
{:ok, value} -> Map.put(params, key, scrub_param(value))
Go to line 373
:error -> params
Go to line 374
end
Go to line 375
end)
Go to line 377
%{conn | params: params}
Go to line 378
end
Go to line 380
defp scrub_param(%{__struct__: mod} = struct) when is_atom(mod) do
Go to line 381
struct
Go to line 382
end
Go to line 384
defp scrub_param(%{} = param) do
Go to line 385
Enum.reduce(param, %{}, fn {k, v}, acc ->
Go to line 386
Map.put(acc, k, scrub_param(v))
Go to line 387
end)
Go to line 388
end
Go to line 390
defp scrub_param(param) when is_list(param) do
Go to line 391
Enum.map(param, &scrub_param/1)
Go to line 392
end
Go to line 394
defp scrub_param(param) do
Go to line 395
if scrub?(param), do: nil, else: param
Go to line 396
end
Go to line 398
defp scrub?(" " <> rest), do: scrub?(rest)
Go to line 399
defp scrub?(""), do: true
Go to line 400
defp scrub?(_), do: false
Go to line 401
end