Elixir
Created by
renews
VS Code or Cursor Broken for Elixir and possible fixes
Additionally, be aware that when you change your version or Elixir or Erlang it will need to rebuild the full PLT. So you will encounter this process again.
/Users/dood/.vscode/extensions/jakebecker.elixir-ls-0.2.25/elixir-ls-release/language_server.sh: line 18: elixir: command not found [Info - 1:13:04 PM] Connection to server got closed. Server will restart. /Users/dood/.vscode/extensions/jakebecker.elixir-ls-0.2.25/elixir-ls-release/language_server.sh: line 18: elixir: command not found [Info - 1:13:04 PM] Connection to server got closed. Server will restart. /Users/dood/.vscode/extensions/jakebecker.elixir-ls-0.2.25/elixir-ls-release/language_server.sh: line 18: elixir: command not found [Info - 1:13:04 PM] Connection to server got closed. Server will restart. /Users/dood/.vscode/extensions/jakebecker.elixir-ls-0.2.25/elixir-ls-release/language_server.sh: line 18: elixir: command not found [Info - 1:13:04 PM] Connection to server got closed. Server will restart. /Users/dood/.vscode/extensions/jakebecker.elixir-ls-0.2.25/elixir-ls-release/language_server.sh: line 18: elixir: command not found [Error - 1:13:04 PM] Connection to server got closed. Server will not be restarted.
asdf reshim elixir <your-version>
Erlang not installed
asdf: No version set for command erl you might want to add one of the following in your .tool-versions file:erlang 21.0.7 erlang 21.1.3 erlang 22.0.7 erlang 19.3.6.9 erlang 22.1.7 [Info - 6:08:00 AM] Connection to server got closed. Server will restart.
Version Mismatch
Started ElixirLS Elixir version: "1.7.4 (compiled with Erlang/OTP 19)" Erlang version: "22" Compiling with Mix env test [Info - 7:41:33 AM] Compile took 224 milliseconds ...
Elixir umbrella projects
How did you launch VS Code?
code .
This opens the current directory in VS Code. This helps isolate environment issues. If it works when launching VS Code from the terminal, consider always doing it that way or look into fixing your environment setup.
ElixirLS stopped working
What happened?
mix do clean, compile
- https://github.com/srcrip/live_toast A beautiful drop-in replacement for the Phoenix Flash system.
- https://github.com/sezaru/flashy Flashy is a small library that extends LiveView's flash support to function and live components.
UI
- https://surface-ui.org/ - git - A server-side rendering component library for Phoenix Framework
mix cmd --app child_app_name mix test --color
Specific file/line
mix cmd --app child_app_name mix test test/child_app_name_nice_test.exs:69 --color
We can also define a Mix task to make our life easier!
def aliases do [ child_test: "cmd --app child_app_name mix test --color" ] end
Then you call
mix child_test
mix child_test test/child_app_name_nice_test.exs:69
- Passwords obtained from previous breaches
- Context-specific words, such as the name of the service, the username, and derivatives thereof
- Repetitive or sequential characters
- Dictionary words
Passwords obtained from previous breaches
def deps do [ ... {:ex_pwned, "~> 0.1.0"} ] end
defmodule MyApp.Users.User do use Ecto.Schema use Pow.Ecto.Schema# ...
def changeset(user_or_changeset, attrs) do user_or_changeset |> pow_changeset(attrs) |> validate_password_breach() end
defp validate_password_breach(changeset) do Ecto.Changeset.validate_change(changeset, :password, fn :password, password -> case password_breached?(password) do true -> [password: "has appeared in a previous breach"] false -> [] end end) end
defp password_breached?(password) do case Mix.env() do :test -> false _any -> ExPwned.password_breached?(password) end end end
Context-specific words, such as the name of the service, the username, and derivatives thereof
defmodule MyApp.Users.User do use Ecto.Schema use Pow.Ecto.Schema# ...
def changeset(user_or_changeset, attrs) do user_or_changeset |> pow_changeset(attrs) |> validate_password_no_context() end
@app_name "My Demo App"
defp validate_password_no_context(changeset) do Ecto.Changeset.validate_change(changeset, :password, fn :password, password -> [ @app_name, String.downcase(@app_name), Ecto.Changeset.get_field(changeset, :email), Ecto.Changeset.get_field(changeset, :username) ] |> Enum.reject(&is_nil/1) |> similar_to_context?(password) |> case do true -> [password: "is too similar to username, email or #{@app_name}"] false -> [] end end) end
def similar_to_context?(contexts, password) do Enum.any?(contexts, &String.jaro_distance(&1, password) > 0.85) end end
Repetitive or sequential characters
defmodule MyApp.Users.User do use Ecto.Schema use Pow.Ecto.Schema# ...
def changeset(user_or_changeset, attrs) do user_or_changeset |> pow_changeset(attrs) |> validate_password() end
defp validate_password(changeset) do changeset |> validate_no_repetitive_characters() |> validate_no_sequential_characters() end
defp validate_no_repetitive_characters(changeset) do Ecto.Changeset.validate_change(changeset, :password, fn :password, password -> case repetitive_characters?(password) do true -> [password: "has repetitive characters"] false -> [] end end) end
defp repetitive_characters?(password) when is_binary(password) do password |> String.to_charlist() |> repetitive_characters?() end defp repetitive_characters?([c, c, c | _rest]), do: true defp repetitive_characters?([_c | rest]), do: repetitive_characters?(rest) defp repetitive_characters?([]), do: false
defp validate_no_sequential_characters(changeset) do Ecto.Changeset.validate_change(changeset, :password, fn :password, password -> case sequential_characters?(password) do true -> [password: "has sequential characters"] false -> [] end end) end
@sequences ["01234567890", "abcdefghijklmnopqrstuvwxyz"] @max_sequential_chars 3
defp sequential_characters?(password) do Enum.any?(@sequences, &sequential_characters?(password, &1)) end
defp sequential_characters?(password, sequence) do max = String.length(sequence) - 1 - @max_sequential_chars
Enum<strong>.</strong>any?(0<strong>..</strong>max, <strong>fn</strong> x <strong>-></strong> pattern <strong>=</strong> String<strong>.</strong>slice(sequence, x, @max_sequential_chars <strong>+</strong> 1) String<strong>.</strong>contains?(password, pattern) <strong>end</strong>)
end end
Dictionary words
defmodule MyApp.Users.User do use Ecto.Schema use Pow.Ecto.Schema# ...
def changeset(user_or_changeset, attrs) do user_or_changeset |> pow_changeset(attrs) |> validate_password_dictionary() end
defp validate_password_dictionary(changeset) do Ecto.Changeset.validate_change(changeset, :password, fn :password, password -> password |> String.downcase() |> password_in_dictionary?() |> case do true -> [password: "is too common"] false -> [] end end) end
:my_app |> :code.priv_dir() |> Path.join("dictionary.txt") |> File.stream!() |> Stream.map(&String.trim/1) |> Stream.each(fn password -> defp password_in_dictionary?(unquote(password)), do: true end) |> Stream.run()
defp password_in_dictionary?(_password), do: false end
Require users to change weak password upon sign in
def check_password(conn, _opts) do changeset = MyApp.Users.User.changeset(%MyApp.Users.User{}, conn.params["user"])case changeset.errors[:password] do nil -> conn
error <strong>-></strong> msg <strong>=</strong> MyAppWeb<strong>.</strong>ErrorHelpers<strong>.</strong>translate_error(error) conn <strong>|></strong> put_flash(:error, "You have to reset your password because it #{msg}") <strong>|></strong> redirect(to: Routes<strong>.</strong>pow_reset_password_reset_password_path(conn, :new)) <strong>|></strong> Plug<strong>.</strong>Conn<strong>.</strong>halt()
end end
Conclusion
Unit tests
defmodule MyApp.Users.UserTest do use MyApp.DataCasealias MyApp.Users.User
test "changeset/2 validates context-specific words" do for invalid <- ["my demo app", "mydemo_app", "mydemoapp1"] do changeset = User.changeset(%User{}, %{"username" => "john.doe", "password" => invalid}) assert changeset.errors[:password] == {"is too similar to username, email or My Demo App", []} end
<em># The below is for email user id</em> changeset <strong>=</strong> User<strong>.</strong>changeset(%User{}, %{"email" <strong>=></strong> "john.doe@example.com", "password" <strong>=></strong> "password12"}) refute changeset<strong>.</strong>errors[:password] for invalid <strong><-</strong> ["john.doe@example.com", "johndoeexamplecom"] <strong>do</strong> changeset <strong>=</strong> User<strong>.</strong>changeset(%User{}, %{"email" <strong>=></strong> "john.doe@example.com", "password" <strong>=></strong> invalid}) assert changeset<strong>.</strong>errors[:password] <strong>==</strong> {"is too similar to username, email or My Demo App", []} <strong>end</strong> <em># The below is for username user id</em> changeset <strong>=</strong> User<strong>.</strong>changeset(%User{}, %{"username" <strong>=></strong> "john.doe", "password" <strong>=></strong> "password12"}) refute changeset<strong>.</strong>errors[:password] for invalid <strong><-</strong> ["john.doe00", "johndoe", "johndoe1"] <strong>do</strong> changeset <strong>=</strong> User<strong>.</strong>changeset(%User{}, %{"username" <strong>=></strong> "john.doe", "password" <strong>=></strong> invalid}) assert changeset<strong>.</strong>errors[:password] <strong>==</strong> {"is too similar to username, email or My Demo App", []} <strong>end</strong>
end
test "changeset/2 validates repetitive and sequential password" do changeset = User.changeset(%User{}, %{"password" => "secret1222"}) assert changeset.errors[:password] == {"has repetitive characters", []}
changeset <strong>=</strong> User<strong>.</strong>changeset(%User{}, %{"password" <strong>=></strong> "secret1223"}) refute changeset<strong>.</strong>errors[:password] changeset <strong>=</strong> User<strong>.</strong>changeset(%User{}, %{"password" <strong>=></strong> "secret1234"}) assert changeset<strong>.</strong>errors[:password] <strong>==</strong> {"has sequential characters", []} changeset <strong>=</strong> User<strong>.</strong>changeset(%User{}, %{"password" <strong>=></strong> "secret1235"}) refute changeset<strong>.</strong>errors[:password] changeset <strong>=</strong> User<strong>.</strong>changeset(%User{}, %{"password" <strong>=></strong> "secretefgh"}) assert changeset<strong>.</strong>errors[:password] <strong>==</strong> {"has sequential characters", []} changeset <strong>=</strong> User<strong>.</strong>changeset(%User{}, %{"password" <strong>=></strong> "secretafgh"}) refute changeset<strong>.</strong>errors[:password]
end end