docs: encrypting attributes how-to guide

This commit is contained in:
Zach Daniel 2024-05-09 21:29:58 -04:00
parent b000259418
commit e81ce83d0e
5 changed files with 143 additions and 2 deletions

View file

@ -97,7 +97,8 @@ Welcome! Here you will find everything you need to know to get started with and
## How-to
- [Optimistic Locking](documentation/how-to/cook-book/preventing-concurrent-writes.livemd)
- [Encrypt Attributes](documentation/how-to/encrypt-attributes.livemd)
- [Prevent Concurrent Writes](documentation/how-to/prevent-concurrent-writes.livemd)
---

View file

@ -0,0 +1,3 @@
.livebook-badge-container + pre {
display: none;
}

View file

@ -0,0 +1,134 @@
<!-- livebook:{"persist_outputs":true} -->
# Encrypt Attributes
```elixir
Mix.install([{:ash, "~> 3.0.0-rc"}, {:ash_cloak, "~> 0.1.0-rc"}, {:cloak, "~> 1.1"}],
consolidate_protocols: false
)
Application.put_env(:my_app, MyApp.Vault,
ciphers: [
default: {
Cloak.Ciphers.AES.GCM,
tag: "AES.GCM.V1",
key: Base.decode64!("ETpvtowVAL7JmcxfqJ+XVQWzKrt1ynAkC0vT7AxfyNU="),
iv_length: 12
}
]
)
defmodule MyApp.Vault do
use Cloak.Vault, otp_app: :my_app
end
MyApp.Vault.start_link()
```
## Introduction
When dealing with PII or other sensitive data, we often want to encrypt this data, and control access to the decrypted values.
To do this in `Ash`, we do that with `AshCloak`. See the getting started guide in `AshCloak` for installation instructions.
## Encrypting attributes
1. If you have not yet, follow the getting started guide for `AshCloak` and `Cloak`
2. Add the `AshCloak` extension to your resource
3. Configure the attributes that should be encrypted
4. Add any other additional desired configuration (provided by `AshCloak`)
## Examples
<!-- livebook:{"disable_formatting":true} -->
```elixir
defmodule User do
use Ash.Resource,
domain: Domain,
data_layer: Ash.DataLayer.Ets,
extensions: [AshCloak]
cloak do
vault MyApp.Vault
attributes [:ssn]
end
attributes do
uuid_primary_key :id
attribute :ssn, :string, allow_nil?: false
end
actions do
defaults [:read, create: [:ssn], update: [:ssn]]
end
end
defmodule Domain do
use Ash.Domain,
validate_config_inclusion?: false
resources do
resource User do
define(:create_user, action: :create, args: [:ssn])
define(:update_user, action: :update, args: [:ssn])
define(:list_users, action: :read)
end
end
end
```
<!-- livebook:{"output":true} -->
```
{:module, Domain, <<70, 79, 82, 49, 0, 1, 255, ...>>,
[
Ash.Domain.Dsl.Resources.Resource,
Ash.Domain.Dsl.Resources.Options,
Ash.Domain.Dsl,
%{opts: [], entities: [...]},
Ash.Domain.Dsl,
Ash.Domain.Dsl.Resources.Options,
...
]}
```
## Data is encrypted when modified and is *not displayed* when inspecting.
```elixir
user = Domain.create_user!("111-11-1111")
```
<!-- livebook:{"output":true} -->
```
#User<
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "bc5284fe-294a-485e-8585-06130a4bca4e",
aggregates: %{},
calculations: %{},
...
>
```
```elixir
# AshCloak turned ssn into a calculation
user.ssn
```
<!-- livebook:{"output":true} -->
```
#Ash.NotLoaded<:calculation, field: :ssn>
```
```elixir
# Load the value to decrypt it on-demand
Ash.load!(user, :ssn).ssn
```
<!-- livebook:{"output":true} -->
```
"111-11-1111"
```

View file

@ -79,7 +79,8 @@ defmodule Ash.MixProject do
"documentation/topics/security/policies.md",
"documentation/topics/reference/glossary.md",
"documentation/topics/reference/expressions.md",
"documentation/how-to/preventing-concurrent-writes.livemd",
"documentation/how-to/encrypt-attributes.livemd",
"documentation/how-to/prevent-concurrent-writes.livemd",
"documentation/dsls/DSL:-Ash.Resource.md",
"documentation/dsls/DSL:-Ash.Domain.md",
"documentation/dsls/DSL:-Ash.Notifier.PubSub.md",
@ -111,6 +112,7 @@ defmodule Ash.MixProject do
~r"documentation/dsls"
]
],
assets: ["documentation/assets"],
skip_undefined_reference_warnings_on: [
"CHANGELOG.md",
"documentation/topics/reference/glossary.md",
@ -132,6 +134,7 @@ defmodule Ash.MixProject do
before_closing_head_tag: fn type ->
if type == :html do
"""
<link rel="stylesheet" href="assets/livebooks.css">
<script>
if (location.hostname === "hexdocs.pm") {
var script = document.createElement("script");