Is it possible to use set_attribute on an encrypted attribute using Ash Cloak?

Hi!

I have in my cloak section in my Resource, defined an encrypted attribute

cloak do
    # the vault to use to encrypt them
    vault(CheckD.DomainModifiers.Security.Vault)

    # the attributes to encrypt
    attributes([:message])

    # This is just equivalent to always providing `load: fields` on all calls
    decrypt_by_default([:message])
  end

I’m trying to do empty the message attribute in an action

update :reject do
      change set_attribute(:message, %{}) # this is an encrypted field
      change set_attribute(:status, :rejected)
      change set_attribute(:actioned_at, &DateTime.utc_now/0)
    end

Is this possible?

Is the attribute a map or a string? What happens when you try to do what you’re doing?

The atttribute is a map. Which is fine, because I wrote my own vault.

I get the error

{:error,
%Ash.Error.Invalid{
changeset: "#Changeset<>",
errors: [
%Ash.Error.Changes.NoSuchAttribute{
resource: CheckD.Domains.MyData.Resources.InboxMessage,
attribute: :message,
splode: Ash.Error,
bread_crumbs: [],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :invalid
}
]
}}

I assume it’s because the corresponding column is encrypted_message in the db, and somehow ash/ash-cloak doesn’t know that during an update.

Ah, right right. So the way you do this is different. We replace the accepted attribute with a correspondingly named argument. What you should do instead is make a custom change that uses AshCloak.encrypt_and_set(changeset, attribute, value)

Thanks. Works great. My implementation below.

My custom Change function

defmodule CheckD.DomainModifiers.SetEncryptedAttribute do
  use Ash.Resource.Change

  def change(changeset, opts, context) do
    changeset |> AshCloak.encrypt_and_set(opts[:attribute], opts[:value])
  end

  def atomic(changeset, opts, context), do: {:ok, change(changeset, opts, context)}
end

My use

update :reject do
      change {CheckD.DomainModifiers.SetEncryptedAttribute, attribute: :message, value: %{}}
      change set_attribute(:status, :rejected)
      change set_attribute(:actioned_at, &DateTime.utc_now/0)
    end

Zach,

now while the above SetEncryptedAttribute works, it is assumed that the input :attribute is configured as encryptable, or else a Runtime Error is thrown.

I think a better option would either be

  1. a Util function is_attribute_encryptable to check if that attribute is configured for encryption. OR
  2. the AshCloak.encrypt_and_set throws a catchable error.

Thoughts?

Makes sense to me for it to raise a specific error. Please open an issue or a PR :man_bowing:

PR submitted.

Cheers!

1 Like