Packages
phoenix
1.6.13
1.8.9
1.8.8
1.8.7
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.1
1.8.0
1.8.0-rc.4
1.8.0-rc.3
1.8.0-rc.2
1.8.0-rc.1
1.8.0-rc.0
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.7.0-rc.3
1.7.0-rc.2
1.7.0-rc.1
1.7.0-rc.0
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-rc.1
1.6.0-rc.0
1.5.15
1.5.14
1.5.13
1.5.12
1.5.11
1.5.10
1.5.9
1.5.8
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.5.0-rc.0
1.4.18
1.4.17
1.4.16
1.4.15
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.3
1.4.0-rc.2
1.4.0-rc.1
1.4.0-rc.0
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.3.0-rc.3
1.3.0-rc.2
1.3.0-rc.1
1.3.0-rc.0
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.1
1.2.0-rc.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.17.1
0.17.0
0.16.1
0.16.0
0.15.0
0.14.0
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.2
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.0
0.4.1
0.4.0
0.3.1
0.3.0
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
Productive. Reliable. Fast. A productive web framework that does not compromise speed or maintainability.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/phx.gen.auth/injector.ex
defmodule Mix.Tasks.Phx.Gen.Auth.Injector do
@moduledoc false
alias Mix.Phoenix.{Context, Schema}
alias Mix.Tasks.Phx.Gen.Auth.HashingLibrary
@type schema :: %Schema{}
@type context :: %Context{schema: schema}
@doc """
Injects a dependency into the contents of mix.exs
"""
@spec mix_dependency_inject(String.t(), String.t()) :: {:ok, String.t()} | :already_injected | {:error, :unable_to_inject}
def mix_dependency_inject(mixfile, dependency) do
with :ok <- ensure_not_already_injected(mixfile, dependency),
{:ok, new_mixfile} <- do_mix_dependency_inject(mixfile, dependency) do
{:ok, new_mixfile}
end
end
@spec do_mix_dependency_inject(String.t(), String.t()) :: {:ok, String.t()} | {:error, :unable_to_inject}
defp do_mix_dependency_inject(mixfile, dependency) do
string_to_split_on = """
defp deps do
[
"""
case split_with_self(mixfile, string_to_split_on) do
{beginning, splitter, rest} ->
new_mixfile = IO.iodata_to_binary([beginning, splitter, " ", dependency, ?,, ?\n, rest])
{:ok, new_mixfile}
_ ->
{:error, :unable_to_inject}
end
end
@doc """
Injects configuration for test environment into `file`.
"""
@spec test_config_inject(String.t(), HashingLibrary.t()) :: {:ok, String.t()} | :already_injected | {:error, :unable_to_inject}
def test_config_inject(file, %HashingLibrary{} = hashing_library) when is_binary(file) do
code_to_inject =
hashing_library
|> test_config_code()
|> normalize_line_endings_to_file(file)
inject_unless_contains(
file,
code_to_inject,
# Matches the entire line and captures the line ending. In the
# replace string:
#
# * the entire matching line is inserted with \\0,
# * the actual code is injected with &2,
# * and the appropriate newlines are injected using \\2.
&Regex.replace(~r/(use Mix\.Config|import Config)(\r\n|\n|$)/, &1, "\\0\\2#{&2}\\2", global: false)
)
end
@doc """
Instructions to provide the user when `test_config_inject/2` fails.
"""
@spec test_config_help_text(String.t(), HashingLibrary.t()) :: String.t()
def test_config_help_text(file_path, %HashingLibrary{} = hashing_library) do
"""
Add the following to #{Path.relative_to_cwd(file_path)}:
#{hashing_library |> test_config_code() |> indent_spaces(4)}
"""
end
defp test_config_code(%HashingLibrary{test_config: test_config}) do
String.trim("""
# Only in tests, remove the complexity from the password hashing algorithm
#{test_config}
""")
end
@router_plug_anchor_line "plug :put_secure_browser_headers"
@doc """
Injects the fetch_current_<schema> plug into router's browser pipeline
"""
@spec router_plug_inject(String.t(), context) :: {:ok, String.t()} | :already_injected | {:error, :unable_to_inject}
def router_plug_inject(file, %Context{schema: schema}) when is_binary(file) do
inject_unless_contains(
file,
router_plug_code(schema),
# Matches the entire line containing `anchor_line` and captures
# the whitespace before the anchor. In the replace string
#
# * the entire matching line is inserted with \\0,
# * the captured indent is inserted using \\1,
# * the actual code is injected with &2,
# * and the appropriate newline is injected using \\2
&Regex.replace(~r/^(\s*)#{@router_plug_anchor_line}.*(\r\n|\n|$)/Um, &1, "\\0\\1#{&2}\\2", global: false)
)
end
@doc """
Instructions to provide the user when `inject_router_plug/2` fails.
"""
@spec router_plug_help_text(String.t(), context) :: String.t()
def router_plug_help_text(file_path, %Context{schema: schema}) do
"""
Add the #{router_plug_name(schema)} plug to the :browser pipeline in #{Path.relative_to_cwd(file_path)}:
pipeline :browser do
...
#{@router_plug_anchor_line}
#{router_plug_code(schema)}
end
"""
end
defp router_plug_code(%Schema{} = schema) do
"plug " <> router_plug_name(schema)
end
defp router_plug_name(%Schema{} = schema) do
":fetch_current_#{schema.singular}"
end
@doc """
Injects a menu in the application layout
"""
@spec app_layout_menu_inject(String.t(), schema) :: {:ok, String.t()} | :already_injected | {:error, :unable_to_inject}
def app_layout_menu_inject(file, %Schema{} = schema) when is_binary(file) do
with {:error, :unable_to_inject} <- app_layout_menu_inject_at_end_of_nav_tag(file, schema),
{:error, :unable_to_inject} <- app_layout_menu_inject_after_opening_body_tag(file, schema) do
{:error, :unable_to_inject}
end
end
@doc """
Instructions to provide the user when `app_layout_menu_inject/2` fails.
"""
@spec app_layout_menu_help_text(String.t(), schema) :: String.t()
def app_layout_menu_help_text(file_path, %Schema{} = schema) do
"""
Add a render call for #{inspect(app_layout_menu_template_name(schema))} to #{Path.relative_to_cwd(file_path)}:
<nav>
#{app_layout_menu_code_to_inject(schema)}
</nav>
"""
end
@doc """
Menu code to inject into the application layout template.
"""
@spec app_layout_menu_code_to_inject(schema) :: String.t()
def app_layout_menu_code_to_inject(%Schema{} = schema) do
"<%= render \"#{app_layout_menu_template_name(schema)}\", assigns %>"
end
@doc """
Name of the template containing the menu
"""
@spec app_layout_menu_template_name(schema) :: String.t()
def app_layout_menu_template_name(%Schema{} = schema) do
"_#{schema.singular}_menu.html"
end
defp app_layout_menu_inject_at_end_of_nav_tag(file, schema) do
inject_unless_contains(
file,
app_layout_menu_code_to_inject(schema),
&Regex.replace(~r/(\s*)<\/nav>/m, &1, "\\1 #{&2}\\0", global: false)
)
end
defp app_layout_menu_inject_after_opening_body_tag(file, schema) do
anchor_line = "<body>"
inject_unless_contains(
file,
app_layout_menu_code_to_inject(schema),
# Matches the entire line containing `anchor_line` and captures
# the whitespace before the anchor. In the replace string, the
# entire matching line is inserted with \\0, then a newline then
# the indent that was captured using \\1. &2 is the code to
# inject.
&Regex.replace(~r/^(\s*)#{anchor_line}.*(\r\n|\n|$)/Um, &1, "\\0\\1 #{&2}\\2", global: false)
)
end
@doc """
Injects code unless the existing code already contains `code_to_inject`
"""
@spec inject_unless_contains(String.t(), String.t(), (String.t(), String.t() -> String.t())) ::
{:ok, String.t()} | :already_injected | {:error, :unable_to_inject}
def inject_unless_contains(code, code_to_inject, inject_fn) when is_binary(code) and is_binary(code_to_inject) and is_function(inject_fn, 2) do
with :ok <- ensure_not_already_injected(code, code_to_inject) do
new_code = inject_fn.(code, code_to_inject)
if code != new_code do
{:ok, new_code}
else
{:error, :unable_to_inject}
end
end
end
@doc """
Injects snippet before the final end in a file
"""
@spec inject_before_final_end(String.t(), String.t()) :: {:ok, String.t()} | :already_injected
def inject_before_final_end(code, code_to_inject) when is_binary(code) and is_binary(code_to_inject) do
if String.contains?(code, code_to_inject) do
:already_injected
else
new_code =
code
|> String.trim_trailing()
|> String.trim_trailing("end")
|> Kernel.<>(code_to_inject)
|> Kernel.<>("end\n")
{:ok, new_code}
end
end
@spec ensure_not_already_injected(String.t(), String.t()) :: :ok | :already_injected
defp ensure_not_already_injected(file, inject) do
if String.contains?(file, inject) do
:already_injected
else
:ok
end
end
@spec split_with_self(String.t(), String.t()) :: {String.t(), String.t(), String.t()} | :error
defp split_with_self(contents, text) do
case :binary.split(contents, text) do
[left, right] -> {left, text, right}
[_] -> :error
end
end
@spec normalize_line_endings_to_file(String.t(), String.t()) :: String.t()
defp normalize_line_endings_to_file(code, file) do
String.replace(code, "\n", get_line_ending(file))
end
@spec get_line_ending(String.t()) :: String.t()
defp get_line_ending(file) do
case Regex.run(~r/\r\n|\n|$/, file) do
[line_ending] -> line_ending
[] -> "\n"
end
end
defp indent_spaces(string, number_of_spaces) when is_binary(string) and is_integer(number_of_spaces) do
indent = String.duplicate(" ", number_of_spaces)
string
|> String.split("\n")
|> Enum.map_join("\n", &(indent <> &1))
end
end