diff --git a/README.md b/README.md index a78468a5..53cfbf2e 100644 --- a/README.md +++ b/README.md @@ -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) --- diff --git a/documentation/assets/livebooks.css b/documentation/assets/livebooks.css new file mode 100644 index 00000000..6a53add7 --- /dev/null +++ b/documentation/assets/livebooks.css @@ -0,0 +1,3 @@ +.livebook-badge-container + pre { + display: none; +} diff --git a/documentation/how-to/encrypt-attributes.livemd b/documentation/how-to/encrypt-attributes.livemd new file mode 100644 index 00000000..7299445f --- /dev/null +++ b/documentation/how-to/encrypt-attributes.livemd @@ -0,0 +1,134 @@ + + +# 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 + + + +```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 +``` + + + +``` +{: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") +``` + + + +``` +#User< + __meta__: #Ecto.Schema.Metadata<:loaded>, + id: "bc5284fe-294a-485e-8585-06130a4bca4e", + aggregates: %{}, + calculations: %{}, + ... +> +``` + +```elixir +# AshCloak turned ssn into a calculation +user.ssn +``` + + + +``` +#Ash.NotLoaded<:calculation, field: :ssn> +``` + +```elixir +# Load the value to decrypt it on-demand +Ash.load!(user, :ssn).ssn +``` + + + +``` +"111-11-1111" +``` diff --git a/documentation/how-to/preventing-concurrent-writes.livemd b/documentation/how-to/prevent-concurrent-writes.livemd similarity index 100% rename from documentation/how-to/preventing-concurrent-writes.livemd rename to documentation/how-to/prevent-concurrent-writes.livemd diff --git a/mix.exs b/mix.exs index 40981238..e561ecec 100644 --- a/mix.exs +++ b/mix.exs @@ -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 """ +