Hello! I have been learning Elixir for the last couple of months, and this past week I have started learning Phoenix with Programming Phoenix by Chris McCord, Bruce Tate, and Jose Valim.
Elixir Version: 1.7.3
Phoenix Version: 1.2.5
I am currently stuck on Creating Users in Chapter 5 (Authenticating Users). In Rumbl.UserController.create(), a user was successfully added to the database by passing in the according changeset to Rumbl.Repo.insert(changeset). When checking the Ecto database with Rumbl.Repo.all(Rumbl.User), the only populated fields within the User structs are for name and username. I don’t expect password to be populated as that field was set to virtual in the schema, no problem there. However, the issue is the password_hash field is not populated (set to nil). Because of this, I cannot use Comeonin.Bcrypt.checkpw() to properly authenticate a user.
I have been struggling with this issue for a couple of hours now and can’t seem to find the right resource to help solve the issue.
Rumbl.UserController.create()
def create(conn, %{"user" => user_params}) do
user_params = for {key, val} <- user_params, into: %{}, do: {String.to_atom(key), val}
changeset = User.registration_changeset(%User{}, user_params)
IO.inspect(changeset)
case Repo.insert(changeset) do
{:ok, user} ->
IO.inspect(user)
conn
|> Rumbl.Auth.login(user)
|> put_flash(:info, "#{user.name} created!")
|> redirect(to: user_path(conn, :index))
{:error, changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
Rumbl.User
defmodule Rumbl.User do
use Rumbl.Web, :model
schema "users" do
field :name, :string
field :username, :string
field :password, :string, virtual: true # virtual fields are NOT persisted to the database
field :password_hash, :string
timestamps()
end
def changeset(model, params \\ :invalid) do
model
|> cast(params, ~w(name username), [])
|> validate_length(:username, min: 1, max: 20)
end
def registration_changeset(model, params) do
model
|> changeset(params)
|> cast(params, ~w(password), [])
|> validate_length(:password, min: 6, max: 100)
|> put_pass_hash()
end
defp put_pass_hash(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{password: pass}} ->
put_change(changeset, :pasword_hash, Comeonin.Bcrypt.hashpwsalt(pass))
_ ->
changeset
end
end
end
Rumbl.Auth
defmodule Rumbl.Auth do
import Plug.Conn
import Comeonin.Bcrypt, only: [checkpw: 2, dummy_checkpw: 0]
def init(opts) do
Keyword.fetch!(opts, :repo)
end
def call(conn, repo) do
user_id = get_session(conn, :user_id)
user = user_id && repo.get(Rumbl.User, user_id)
assign(conn, :current_user, user)
end
def login(conn, user) do
conn
|> assign(:current_user, user)
|> put_session(:user_id, user.id)
|> configure_session(renew: true)
end
def login_by_username_and_pass(conn, username, given_pass, opts) do
repo = Keyword.fetch!(opts, :repo)
user = repo.get_by(Rumbl.User, username: username)
IO.puts("Given Pass: #{given_pass}")
IO.puts("Password Hash: #{inspect user.password_hash}")
cond do
user && checkpw(given_pass, user.password_hash) ->
{:ok, login(conn, user)}
user ->
{:error, :unauthorized, conn}
true ->
dummy_checkpw()
{:error, :not_found, conn}
end
end
end

.



















