Alexandre S Hostert
Alexandre S Hostert

Reputation: 340

Got error when trying to Fernet.Ecto.String.dump in test

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?

Additional (not so important/relevant) info:

Upvotes: 0

Views: 45

Answers (1)

Ryan Bigg
Ryan Bigg

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

Related Questions