Reputation: 340
I'm trying to encrypt a String
using Fernet.Ecto.String.dump
this way...
defp encrypt_password(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{password: password}} ->
put_change(changeset, :password_hash, Fernet.Ecto.String.dump(password))
_ ->
changeset
end
end
... in this automatically generated test (mix phoenix.gen.json User users...
)...
@valid_attrs %{email: "[email protected]", password: "q1w2e3"}
test "creates and renders resource when data is valid", %{conn: conn} do
conn = post conn, user_path(conn, :create), user: @valid_attrs
body = json_response(conn, 201)
|> IO.inspect
assert body["data"]["id"]
assert body["data"]["email"]
assert body["data"]["password"]
assert body["data"]["password_hash"]
assert Repo.get_by(User, email: "[email protected]")
end
... and got this:
1) test creates and renders resource when data is valid (Api.UserControllerTest)
test/controllers/user_controller_test.exs:32
** (ArgumentError) argument error
stacktrace:
:erlang.byte_size({:ok, "gAAAAABYzq2ofkNpJauc1vIDNrze1ORAMJXW0j0WN5bDong-cpgYYfaV75RYbX3A5yPl4fo86ctkZfszI8ePXGMejW50eRnfeg=="})
(fernetex) lib/fernetex.ex:193: Fernet.pad/1
(fernetex) lib/fernetex.ex:176: Fernet.encrypt/3
(fernetex) lib/fernetex.ex:154: Fernet.generate/4
(fernet_ecto) lib/fernet_ecto/type.ex:9: Fernet.Ecto.Type.encrypt/2
(ecto) lib/ecto/type.ex:662: Ecto.Type.process_dumpers/3
(ecto) lib/ecto/repo/schema.ex:695: Ecto.Repo.Schema.dump_field!/6
(ecto) lib/ecto/repo/schema.ex:708: anonymous fn/6 in Ecto.Repo.Schema.dump_fields!/5
(stdlib) lists.erl:1263: :lists.foldl/3
(ecto) lib/ecto/repo/schema.ex:706: Ecto.Repo.Schema.dump_fields!/5
(ecto) lib/ecto/repo/schema.ex:655: Ecto.Repo.Schema.dump_changes!/6
(ecto) lib/ecto/repo/schema.ex:200: anonymous fn/13 in Ecto.Repo.Schema.do_insert/4
(api) web/controllers/user_controller.ex:16: Api.UserController.create/2
(api) web/controllers/user_controller.ex:1: Api.UserController.action/2
(api) web/controllers/user_controller.ex:1: Api.UserController.phoenix_controller_pipeline/2
(api) lib/api/endpoint.ex:1: Api.Endpoint.instrument/4
(api) lib/phoenix/router.ex:261: Api.Router.dispatch/2
(api) web/router.ex:1: Api.Router.do_call/2
(api) lib/api/endpoint.ex:1: Api.Endpoint.phoenix_pipeline/1
(api) lib/api/endpoint.ex:1: Api.Endpoint.call/2
When I used the command in iex -S mix phoenix.server
, it works ok:
iex(15)> Fernet.Ecto.String.dump("teste")
{:ok, "gAAAAABYzq-iDjQb5i1MYY8RXbFl9ZFfDQp2wyVWqAdPJIVKgMtFap-P8AtuvsNXqrcedp5CGaJjS9M2kBD60OPOR4l_tkDO8w=="}
iex(16)>
If I avoid the encryption with put_change(changeset, :password_hash, password)
, the test will pass.
This is the structure in the DB:
schema "users" do
field :name, :string
field :email, :string
field :password, :string, virtual: true
field :password_hash, Fernet.Ecto.String
According to documentation, Fernet.Ecto.String
is stored in :binary
column in the database:
add :password_hash, :binary
Can anyone help me to figure out why this test is failing?
Documentation about Fernet, in case anyone wants to known.
Why I'm trying this instead of using comeonin
: JWT (JSON Web Tokens) is a Bad Standard That Everyone Should Avoid - Paragon Initiative Enterprises Blog
I did post an issue on fernet_ecto github (with less details).
Upvotes: 0
Views: 45
Reputation: 107728
I believe this is because Fernet.Ecto.String.dump(password)
returns a tuple of {:ok, "hash-goes-here"}
, and you're trying to assign this tuple to a string field.
You should try this:
defp encrypt_password(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{password: password}} ->
{:ok, hash} = Fernet.Ecto.String.dump(password)
put_change(changeset, :password_hash, hash)
_ ->
changeset
end
end
This way, it will only be assigning the hash to the field.
Upvotes: 2