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

Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
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
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
Kurisu
For example for a current url like http://localhost:4000/cosmetic/products?_utf8=✓&query=perfume&page=2, I would like to get: ...
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
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
marick
I had some trouble figuring out how to make many-to-many associations work. Once I got it working, I wrote a blog post. Because I’m a nov...
New

Other popular topics Top

New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New

We're in Beta

About us Mission Statement