cloak icon indicating copy to clipboard operation
cloak copied to clipboard

Support encrypted fields in embedded schemas

Open danielberkompas opened this issue 6 years ago • 4 comments

See #81 and #83.

Because many databases do not support direct encoding and decoding of embeds, it is often emulated by Ecto by using specific encoding and decoding rules.

For example, PostgreSQL will store embeds on top of JSONB columns, which means types in embedded schemas won’t go through the usual dump->DB->load cycle but rather encode->DB->decode->cast. This means that, when using embedded schemas with databases like PG or MySQL, make sure all of your types can be JSON encoded/decoded correctly. Ecto provides this guarantee for all built-in types.

Some thoughts:

  • The values would need to be base64 encrypted in order to encode to JSON
  • Ecto needs to call Cloak's dump functions in order to trigger the encryption

danielberkompas avatar Oct 04 '18 21:10 danielberkompas

I have posted a feature request in Ecto's Google group asking for a needed feature to support this: https://groups.google.com/d/msg/elixir-ecto/RbRdj2NFfDI/qsRLz15nAQAJ

danielberkompas avatar Oct 04 '18 21:10 danielberkompas

I'd love to see this. Just yesterday I tried using encrypted binaries on an embedded schema and then checked my database... not encrypted.

Another option that would work in my case, would be to use an encrypted Map as the field Ecto stores the embedded_schema in. Not sure how that would work with Ecto needing a map type though.

jc00ke avatar Mar 26 '19 20:03 jc00ke

This feature is now in Ecto. Just add:

def embed_as(_format), do: :dump

To the Ecto type and it should work in future Ecto versions.

josevalim avatar Jul 24 '19 20:07 josevalim

Tested this successfully on our app by decoding/encoding the values with 64 as suggested on the initial comment here:

defmodule MyApp.EncodedBinary do
  use Cloak.Ecto.Binary, vault: MyApp.Vault

  def embed_as(:json), do: :dump

  def dump(nil), do: {:ok, nil}

  def dump(value) do
    with {:ok, encrypted} <- super(value) do
      {:ok, Base.encode64(encrypted)}
    end
  end

  def load(nil), do: {:ok, nil}

  def load(value), do: super(Base.decode64!(value))
end

lucasmazza avatar Jul 24 '19 21:07 lucasmazza