kevinschweikert

kevinschweikert

Ash encryption for resource attribute

Hi there,

is there a recommended way to encrypt/decrypt fields of a resource to the database?

I’ve used Cloak before, but i struggle to understand, how this would integrate into Ash.
It would be really nice if it worked like Cloak and with that I mean, that it encrypts/decrypts the data automatically when inserting/reading.

Marked As Solved

zachdaniel

zachdaniel

Creator of Ash

In AshHq we use cloak to encrypt data. We do it with a combination of changes and calculations.

For instance:

attribute :encrypted_name, :string
attribute :encrypted_address, :string

Then, in actions we write to those attributes using arguments:

    update :update_merch_settings do
      argument :address, :string
      argument :name, :string

      accept [:shirt_size]
      change set_attribute(:encrypted_address, arg(:address))
      change set_attribute(:encrypted_name, arg(:name))
    end
  end

We don’t write to the encrypted attributes directly to prevent double encryption.

This is what our encryption change looks like:

defmodule AshHq.Changes.Encrypt do
  @moduledoc "Encrypt attributes that are being changed before submitting the action"
  use Ash.Resource.Change

  def change(changeset, opts, _) do
    Ash.Changeset.before_action(changeset, fn changeset ->
      Enum.reduce(opts[:fields], changeset, fn field, changeset ->
        case Ash.Changeset.fetch_change(changeset, field) do
          {:ok, value} when is_binary(value) ->
            new_value =
              value
              |> AshHq.Vault.encrypt!()
              |> Base.encode64()

            Ash.Changeset.force_change_attribute(changeset, field, new_value)

          _ ->
            changeset
        end
      end)
    end)
  end
end

And we apply it on every action like so:

  changes do
    ...
    change {AshHq.Changes.Encrypt, fields: [:encrypted_address, :encrypted_name]}
  end

Then, we can decrypt it on demand using calculations:

  calculations do
    calculate :address, :string, {Decrypt, field: :encrypted_address}
    calculate :name, :string, {Decrypt, field: :encrypted_name}
  end

And that calculation looks like this:

defmodule AshHq.Calculations.Decrypt do
  @moduledoc "Decrypts a given value on demand"
  use Ash.Calculation

  @impl Ash.Calculation
  def calculate(records, opts, _) do
    {:ok,
     Enum.map(records, fn record ->
       record
       |> Map.get(opts[:field])
       |> case do
         nil ->
           nil

         value ->
           value
           |> Base.decode64!()
           |> AshHq.Vault.decrypt!()
       end
     end)}
  end

  @impl Ash.Calculation
  def load(_, opts, _) do
    [opts[:field]]
  end
end

This was a very early addition to AshHq. I’d love to see an encryption extension at some point :slight_smile:

Also Liked

jimsynz

jimsynz

Ash Core Team

We also use this calculation based approach in a client app and it’s very handy because you have to explicitly ask for the decrypted value (via a load) and due to our policy configuration you have to provide an actor which also gives us a place to add audit logging at a later date.

mudspot

mudspot

Brilliant!

I managed to implement it with my custom Vault (without Cloak)

Thank you!

Where Next?

Popular in Questions Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New

We're in Beta

About us Mission Statement