I’ve got the same error (so frustrating!):
Protocol.UndefinedError at POST /login
protocol Enumerable not implemented for %Dog.UserManager.User{meta: #Ecto.Schema.Metadata<:loaded, “users”>, id: 3, inserted_at: ~N[2019-07-15 05:56:22], password: “$argon2id$v=19$m=131072,t=8,p=4$jinhp9gEDVENO2PIdm5zkg$ovideTURD59gCoqtE6UUuYZ/32YMnpgppuRf0NFAJ/k”, updated_at: ~N[2019-07-15 05:56:22], username: “me”}. This protocol is implemented for: Ecto.Adapters.SQL.Stream, Postgrex.Stream, DBConnection.Stream, DBConnection.PrepareStream, HashSet, Range, Map, Function, List, Stream, Date.Range, HashDict, GenEvent.Stream, MapSet, File.Stream, IO.Stream
The stack trace seems to point to my SessionController:
* elixir /home/build/elixir/lib/elixir/lib/enum.ex:1Enumerable.impl_for!/1
* elixir /home/build/elixir/lib/elixir/lib/enum.ex:141Enumerable.reduce/3
* elixir lib/enum.ex:3015Enum.reverse/1
* elixir lib/enum.ex:2647Enum.to_list/1
* elixir lib/map.ex:181Map.new_from_enum/1
* guardian lib/guardian.ex:573Guardian.encode_and_sign/4
* guardian lib/guardian/plug.ex:208Guardian.Plug.sign_in/5
* lib/dog_web/controllers/session_controller.ex:29DogWeb.SessionController.login_reply/2
* lib/dog_web/controllers/session_controller.ex:1DogWeb.SessionController.action/2
* lib/dog_web/controllers/session_controller.ex:1DogWeb.SessionController.phoenix_controller_pipeline/2
* phoenix lib/phoenix/router.ex:288Phoenix.Router.__call__/2
* lib/dog_web/endpoint.ex:1DogWeb.Endpoint.plug_builder_call/2
* lib/plug/debugger.ex:122DogWeb.Endpoint."call (overridable 3)"/2
* lib/dog_web/endpoint.ex:1DogWeb.Endpoint.call/2
* phoenix lib/phoenix/endpoint/cowboy2_handler.ex:40Phoenix.Endpoint.Cowboy2Handler.init/2
* cowboy /Users/7stud/phoenix_apps/dog/deps/cowboy/src/cowboy_handler.erl:41:cowboy_handler.execute/2
* cowboy /Users/7stud/phoenix_apps/dog/deps/cowboy/src/cowboy_stream_h.erl:296:cowboy_stream_h.execute/3
* cowboy /Users/7stud/phoenix_apps/dog/deps/cowboy/src/cowboy_stream_h.erl:274:cowboy_stream_h.request_process/3
Here’s my SessionController:
defmodule DogWeb.SessionController do
use DogWeb, :controller
alias Dog.{UserManager, UserManager.User, UserManager.Guardian}
def new(conn, _) do
changeset = UserManager.change_user(%User{})
maybe_user = Guardian.Plug.current_resource(conn)
if maybe_user do
redirect(conn, to: "/secret")
else
render(conn, "new.html",
changeset: changeset,
action: Routes.session_path(conn, :login)
)
end
end
def login(conn,
%{"user" => %{"username" => username,
"password" => password}})
do
UserManager.authenticate_user(username, password)
|> login_reply(conn)
end
def login_reply({:ok, user}, conn) do
conn
|> put_flash(:info, "Welcome back!")
|> Guardian.Plug.sign_in(Guardian, user)
|> redirect(to: "/secret")
end
def login_reply({:error, reason}, conn) do
conn
|> put_flash(:error, to_string(reason))
|> new(%{})
end
def logout(conn, _) do
conn
|> Guardian.Plug.sign_out(Guardian)
|> redirect(to: "/login")
end
end
I checked the Guardian.Plug docs for the sign_in() function:
sign_in(conn, impl, resource, claims \\ %{}, opts \\ [])
sign_in(
Plug.Conn.t(),
module(),
any(),
Guardian.Token.claims(),
Guardian.options()
) :: Plug.Conn.t()
And, I"m calling sign_in() with the correct types for the arguments. I’m stuck.
Here’s my config.exs:
use Mix.Config
config :dog,
ecto_repos: [Dog.Repo]
config :dog, Dog.Repo,
datbase: "dog",
username: "7stud",
password: "",
hostname: "localhost",
port: "5432"
config :dog, Dog.UserManager.Guardian,
issuer: "dog",
secret_key: "e8UPW0Doh0iBonm8ZK0RJVzaAA8r+Jgw4t3lVEoB1SiByke/V9cxcz//1iPkpVrq"
# Configures the endpoint
config :dog, DogWeb.Endpoint,
url: [host: "localhost"],
secret_key_base: "xpVFSaAFJZ8xUEhWLrzT/sTvL/Z6y+mSvfobiKiKanWexNwL2Wj2EbRXr7V9fQSj",
render_errors: [view: DogWeb.ErrorView, accepts: ~w(html json)],
pubsub: [name: Dog.PubSub, adapter: Phoenix.PubSub.PG2]
# Configures Elixir's Logger
config :logger, :console,
format: "$time $metadata[$level] $message\n",
metadata: [:request_id]
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"
I’m using a UserManager context for the public interface:
defmodule Dog.UserManager do
@repo Dog.Repo
alias Dog.UserManager.User
alias Argon2
import Ecto.Query, only: [from: 2]
def list_users() do
@repo.all(User)
end
def get_user(id) do
@repo.get!(User, id)
end
def change_user(%User{}=user) do
User.changeset(user, %{})
end
def insert_user(attrs) do
%User{}
|> User.changeset(attrs)
|> @repo.insert()
end
def delete_user(%User{}=user) do
@repo.delete(user)
end
def authenticate_user(username, plain_text_password) do
query = from u in User, where: u.username == ^username
case @repo.one(query) do
nil ->
Argon2.no_user_verify()
{:error, :invalid_credentials}
user ->
if Argon2.verify_pass(plain_text_password, user.password) do
{:ok, user}
else
{:error, :invalid_credentials}
end
end
end
end
Here’s my directory structure:
~/phoenix_apps/dog$ tree lib
lib
├── dog
│ ├── application.ex
│ ├── repo.ex
│ └── user_manager
│ ├── error_handler.ex
│ ├── guardian.ex
│ ├── pipeline.ex
│ ├── user.ex
│ └── user_manager.ex
├── dog.ex
├── dog_web
│ ├── channels
│ │ └── user_socket.ex
│ ├── controllers
│ │ ├── page_controller.ex
│ │ └── session_controller.ex
│ ├── endpoint.ex
│ ├── gettext.ex
│ ├── router.ex
│ ├── templates
│ │ ├── layout
│ │ │ └── app.html.eex
│ │ ├── page
│ │ │ ├── index.html.eex
│ │ │ └── protected.html
│ │ └── session
│ │ └── new.html.eex
│ └── views
│ ├── error_helpers.ex
│ ├── error_view.ex
│ ├── layout_view.ex
│ ├── page_view.ex
│ └── session_view.ex
└── dog_web.ex






















