diff --git a/.formatter.exs b/.formatter.exs
index 53d5a19..e11fe37 100644
--- a/.formatter.exs
+++ b/.formatter.exs
@@ -1,7 +1,6 @@
spark_locals_without_parens = [
access_token_attribute_name: 1,
access_token_expires_at_attribute_name: 1,
- api: 1,
auth0: 0,
auth0: 1,
auth0: 2,
@@ -21,6 +20,7 @@ spark_locals_without_parens = [
confirmation_required?: 1,
confirmed_at_field: 1,
destroy_action_name: 1,
+ domain: 1,
enabled?: 1,
expunge_expired_action_name: 1,
expunge_interval: 1,
@@ -106,7 +106,8 @@ spark_locals_without_parens = [
]
[
- import_deps: [:ash, :spark, :ash_json_api, :ash_graphql],
+ # , :ash_json_api, :ash_graphql],
+ import_deps: [:ash, :spark],
inputs: [
"*.{ex,exs}",
"{dev,config,lib,test}/**/*.{ex,exs}"
diff --git a/config/dev.exs b/config/dev.exs
index 1140312..ce0132d 100644
--- a/config/dev.exs
+++ b/config/dev.exs
@@ -13,7 +13,7 @@ config :git_ops,
config :ash_authentication, DevServer, start?: true, port: 4000
-config :ash_authentication, ecto_repos: [Example.Repo], ash_apis: [Example]
+config :ash_authentication, ecto_repos: [Example.Repo], ash_domains: [Example]
config :ash_authentication, Example.Repo,
username: "postgres",
diff --git a/config/test.exs b/config/test.exs
index 15156b5..b19f81d 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -1,6 +1,6 @@
import Config
-config :ash_authentication, ecto_repos: [Example.Repo], ash_apis: [Example]
+config :ash_authentication, ecto_repos: [Example.Repo], ash_domains: [Example]
config :ash_authentication, Example.Repo,
username: "postgres",
diff --git a/dev/dev_server/api_router.ex b/dev/dev_server/api_router.ex
index eb2da81..786f81d 100644
--- a/dev/dev_server/api_router.ex
+++ b/dev/dev_server/api_router.ex
@@ -1,14 +1,14 @@
-defmodule DevServer.ApiRouter do
- @moduledoc """
- Router for API Requests.
- """
- use Plug.Router
- import Example.AuthPlug
+# defmodule DevServer.ApiRouter do
+# @moduledoc """
+# Router for API Requests.
+# """
+# use Plug.Router
+# import Example.AuthPlug
- plug(:load_from_bearer)
- plug(:set_actor, :user)
- plug(:match)
- plug(:dispatch)
+# plug(:load_from_bearer)
+# plug(:set_actor, :user)
+# plug(:match)
+# plug(:dispatch)
- forward("/", to: DevServer.JsonApiRouter)
-end
+# forward("/", to: DevServer.JsonApiRouter)
+# end
diff --git a/dev/dev_server/gql_router.ex b/dev/dev_server/gql_router.ex
index 1fe3c4a..b3c29c9 100644
--- a/dev/dev_server/gql_router.ex
+++ b/dev/dev_server/gql_router.ex
@@ -1,26 +1,26 @@
-defmodule DevServer.GqlRouter do
- @moduledoc """
- Router for GraphQL requests.
- """
- use Plug.Router
- import Example.AuthPlug
+# defmodule DevServer.GqlRouter do
+# @moduledoc """
+# Router for GraphQL requests.
+# """
+# use Plug.Router
+# import Example.AuthPlug
- plug(:load_from_bearer)
- plug(:set_actor, :user)
- plug(AshGraphql.Plug)
- plug(:match)
- plug(:dispatch)
+# plug(:load_from_bearer)
+# plug(:set_actor, :user)
+# plug(AshGraphql.Plug)
+# plug(:match)
+# plug(:dispatch)
- forward("/playground",
- to: Absinthe.Plug.GraphiQL,
- init_opts: [
- schema: Example.Schema,
- interface: :playground
- ]
- )
+# forward("/playground",
+# to: Absinthe.Plug.GraphiQL,
+# init_opts: [
+# schema: Example.Schema,
+# interface: :playground
+# ]
+# )
- forward("/",
- to: Absinthe.Plug,
- init_opts: [schema: Example.Schema]
- )
-end
+# forward("/",
+# to: Absinthe.Plug,
+# init_opts: [schema: Example.Schema]
+# )
+# end
diff --git a/dev/dev_server/json_api_router.ex b/dev/dev_server/json_api_router.ex
index 6ab03b9..5ac39b6 100644
--- a/dev/dev_server/json_api_router.ex
+++ b/dev/dev_server/json_api_router.ex
@@ -1,4 +1,4 @@
-defmodule DevServer.JsonApiRouter do
- @moduledoc false
- use AshJsonApi.Api.Router, api: Example
-end
+# defmodule DevServer.JsonApiRouter do
+# @moduledoc false
+# use AshJsonApi.Api.Router, api: Example
+# end
diff --git a/dev/dev_server/router.ex b/dev/dev_server/router.ex
index 2a43c19..4bea6a0 100644
--- a/dev/dev_server/router.ex
+++ b/dev/dev_server/router.ex
@@ -13,7 +13,7 @@ defmodule DevServer.Router do
forward("/auth", to: Example.AuthPlug)
get("/clear_session", to: DevServer.ClearSession)
post("/token_check", to: DevServer.TokenCheck)
- forward("/api", to: DevServer.ApiRouter)
- forward("/gql", to: DevServer.GqlRouter)
+ # forward("/api", to: DevServer.ApiRouter)
+ # forward("/gql", to: DevServer.GqlRouter)
forward("/", to: DevServer.WebRouter)
end
diff --git a/dev/dev_server/test_page.html.eex b/dev/dev_server/test_page.html.eex
index fd3e2ed..0d25f8c 100644
--- a/dev/dev_server/test_page.html.eex
+++ b/dev/dev_server/test_page.html.eex
@@ -10,7 +10,7 @@
Resources:
<%= for {resource, options, strategies} <- @resources do %>
- <%= inspect(options.subject_name) %> - <%= Ash.Api.Info.short_name(options.api) %> / <%= Ash.Resource.Info.short_name(resource) %>
+ <%= inspect(options.subject_name) %> - <%= Ash.Domain.Info.short_name(options.api) %> / <%= Ash.Resource.Info.short_name(resource) %>
<%= for strategy <- strategies do %>
diff --git a/documentation/dsls/DSL:-AshAuthentication.AddOn.Confirmation.md b/documentation/dsls/DSL:-AshAuthentication.AddOn.Confirmation.md
index 9f8e263..faf477a 100644
--- a/documentation/dsls/DSL:-AshAuthentication.AddOn.Confirmation.md
+++ b/documentation/dsls/DSL:-AshAuthentication.AddOn.Confirmation.md
@@ -22,7 +22,8 @@ minimum requirements:
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
- extensions: [AshAuthentication]
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
attributes do
uuid_primary_key :id
@@ -30,8 +31,6 @@ defmodule MyApp.Accounts.User do
end
authentication do
- api MyApp.Accounts
-
add_ons do
confirmation :confirm do
monitor_fields [:email]
diff --git a/documentation/dsls/DSL:-AshAuthentication.Strategy.MagicLink.md b/documentation/dsls/DSL:-AshAuthentication.Strategy.MagicLink.md
index a8f90bc..e68578e 100644
--- a/documentation/dsls/DSL:-AshAuthentication.Strategy.MagicLink.md
+++ b/documentation/dsls/DSL:-AshAuthentication.Strategy.MagicLink.md
@@ -19,7 +19,8 @@ There are other options documented in the DSL.
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
- extensions: [AshAuthentication]
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
attributes do
uuid_primary_key :id
@@ -27,8 +28,6 @@ defmodule MyApp.Accounts.User do
end
authentication do
- api MyApp.Accounts
-
strategies do
magic_link do
identity_field :email
diff --git a/documentation/dsls/DSL:-AshAuthentication.Strategy.OAuth2.md b/documentation/dsls/DSL:-AshAuthentication.Strategy.OAuth2.md
index 0e3a7d1..44596ae 100644
--- a/documentation/dsls/DSL:-AshAuthentication.Strategy.OAuth2.md
+++ b/documentation/dsls/DSL:-AshAuthentication.Strategy.OAuth2.md
@@ -20,7 +20,8 @@ the following minimum criteria:
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
- extensions: [AshAuthentication]
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
attributes do
uuid_primary_key :id
@@ -28,8 +29,6 @@ defmodule MyApp.Accounts.User do
end
authentication do
- api MyApp.Accounts
-
strategies do
oauth2 :example do
client_id "OAuth Client ID"
@@ -158,8 +157,6 @@ defmodule MyApp.Accounts.User do
end
authentication do
- api MyApp.Accounts
-
strategies do
oauth2 :example do
registration_enabled? false
@@ -196,8 +193,6 @@ defmodule MyApp.Accounts.User do
end
authentication do
- api MyApp.Accounts
-
strategies do
oauth2 :example do
end
diff --git a/documentation/dsls/DSL:-AshAuthentication.Strategy.Password.md b/documentation/dsls/DSL:-AshAuthentication.Strategy.Password.md
index 0f0c394..cdda8f7 100644
--- a/documentation/dsls/DSL:-AshAuthentication.Strategy.Password.md
+++ b/documentation/dsls/DSL:-AshAuthentication.Strategy.Password.md
@@ -19,7 +19,8 @@ There are other options documented in the DSL.
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
- extensions: [AshAuthentication]
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
attributes do
uuid_primary_key :id
@@ -28,8 +29,6 @@ defmodule MyApp.Accounts.User do
end
authentication do
- api MyApp.Accounts
-
strategies do
password :password do
identity_field :email
diff --git a/documentation/dsls/DSL:-AshAuthentication.TokenResource.md b/documentation/dsls/DSL:-AshAuthentication.TokenResource.md
index 412092c..6860a97 100644
--- a/documentation/dsls/DSL:-AshAuthentication.TokenResource.md
+++ b/documentation/dsls/DSL:-AshAuthentication.TokenResource.md
@@ -30,11 +30,8 @@ system to function.
defmodule MyApp.Accounts.Token do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
- extensions: [AshAuthentication.TokenResource]
-
- token do
- api MyApp.Accounts
- end
+ extensions: [AshAuthentication.TokenResource],
+ domain: MyApp.Accounts
postgres do
table "tokens"
@@ -70,7 +67,7 @@ Configuration options for this token resource
| Name | Type | Default | Docs |
|------|------|---------|------|
-| [`api`](#token-api){: #token-api } | `module` | | The Ash API to use to access this resource. |
+| [`domain`](#token-domain){: #token-domain } | `module` | | The Ash domain to use to access this resource. |
| [`expunge_expired_action_name`](#token-expunge_expired_action_name){: #token-expunge_expired_action_name } | `atom` | `:expunge_expired` | The name of the action used to remove expired tokens. |
| [`read_expired_action_name`](#token-read_expired_action_name){: #token-read_expired_action_name } | `atom` | `:read_expired` | The name of the action use to find all expired tokens. |
| [`expunge_interval`](#token-expunge_interval){: #token-expunge_interval } | `pos_integer` | `12` | How often to scan this resource for records which have expired, and thus can be removed. |
diff --git a/documentation/dsls/DSL:-AshAuthentication.UserIdentity.md b/documentation/dsls/DSL:-AshAuthentication.UserIdentity.md
index b4b16fc..18ba234 100644
--- a/documentation/dsls/DSL:-AshAuthentication.UserIdentity.md
+++ b/documentation/dsls/DSL:-AshAuthentication.UserIdentity.md
@@ -29,10 +29,10 @@ unlikely that you will need to customise it.
defmodule MyApp.Accounts.UserIdentity do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
- extensions: [AshAuthentication.UserIdentity]
+ extensions: [AshAuthentication.UserIdentity],
+ domain: MyApp.Accounts
user_identity do
- api MyApp.Accounts
user_resource MyApp.Accounts.User
end
@@ -60,7 +60,7 @@ Configure identity options for this resource
| Name | Type | Default | Docs |
|------|------|---------|------|
| [`user_resource`](#user_identity-user_resource){: #user_identity-user_resource .spark-required} | `module` | | The user resource to which these identities belong. |
-| [`api`](#user_identity-api){: #user_identity-api } | `module` | | The Ash API to use to access this resource. |
+| [`domain`](#user_identity-domain){: #user_identity-domain } | `module` | | The Ash domain to use to access this resource. |
| [`uid_attribute_name`](#user_identity-uid_attribute_name){: #user_identity-uid_attribute_name } | `atom` | `:uid` | The name of the `uid` attribute on this resource. |
| [`strategy_attribute_name`](#user_identity-strategy_attribute_name){: #user_identity-strategy_attribute_name } | `atom` | `:strategy` | The name of the `strategy` attribute on this resource. |
| [`user_id_attribute_name`](#user_identity-user_id_attribute_name){: #user_identity-user_id_attribute_name } | `atom` | `:user_id` | The name of the `user_id` attribute on this resource. |
diff --git a/documentation/dsls/DSL:-AshAuthentication.md b/documentation/dsls/DSL:-AshAuthentication.md
index f5b6c2c..2b7e122 100644
--- a/documentation/dsls/DSL:-AshAuthentication.md
+++ b/documentation/dsls/DSL:-AshAuthentication.md
@@ -17,7 +17,8 @@ the `AshAuthentication` extension on your resource:
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
- extensions: [AshAuthentication]
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
attributes do
uuid_primary_key :id
@@ -26,8 +27,6 @@ defmodule MyApp.Accounts.User do
end
authentication do
- api MyApp.Accounts
-
strategies do
password :password do
identity_field :email
@@ -101,7 +100,7 @@ Configure authentication for this resource
| Name | Type | Default | Docs |
|------|------|---------|------|
| [`subject_name`](#authentication-subject_name){: #authentication-subject_name } | `atom` | | The subject name is used anywhere that a short version of your resource name is needed. Must be unique system-wide and will be inferred from the resource name by default (ie `MyApp.Accounts.User` -> `user`). |
-| [`api`](#authentication-api){: #authentication-api } | `module` | | The name of the Ash API to use to access this resource when doing anything authenticaiton related. |
+| [`domain`](#authentication-domain){: #authentication-domain } | `module` | | The name of the Ash domain to use to access this resource when doing anything authentication related. |
| [`get_by_subject_action_name`](#authentication-get_by_subject_action_name){: #authentication-get_by_subject_action_name } | `atom` | `:get_by_subject` | The name of the read action used to retrieve records. If the action doesn't exist, one will be generated for you. |
| [`select_for_senders`](#authentication-select_for_senders){: #authentication-select_for_senders } | `list(atom)` | | A list of fields that we will ensure are selected whenever a sender will be invoked. Defaults to `[:email]` if there is an `:email` attribute on the resource, and `[]` otherwise. |
diff --git a/documentation/topics/custom-strategy.md b/documentation/topics/custom-strategy.md
index 550709e..8d7c74f 100644
--- a/documentation/topics/custom-strategy.md
+++ b/documentation/topics/custom-strategy.md
@@ -4,30 +4,30 @@ AshAuthentication allows you to bring your own authentication strategy without
having to change the Ash Authentication codebase.
> There is functionally no difference between "add ons" and "strategies" other
-> than where they appear in the DSL. We invented "add ons" because it felt
+> than where they appear in the DSL. We invented "add ons" because it felt
> weird calling "confirmation" an authentication strategy.
There are several moving parts which must all work together so hold on to your hat!
- 1. A `Spark.Dsl.Entity` struct. This is used to define the strategy DSL
- inside the `strategies` (or `add_ons`) section of the `authentication` DSL.
- 2. A strategy struct, which stores information about the strategy as
- configured on a resource which must comply with a few rules.
- 3. An optional transformer, which can be used to manipulate the DSL state of
- the entity and the resource.
- 4. An optional verifier, which can be used to verify the DSL state of the
- entity and the resource after compilation.
- 4. The `AshAuthentication.Strategy` protocol, which provides the glue needed
- for everything to wire up and wrappers around the actions needed to run on
- the resource.
+1. A `Spark.Dsl.Entity` struct. This is used to define the strategy DSL
+ inside the `strategies` (or `add_ons`) section of the `authentication` DSL.
+2. A strategy struct, which stores information about the strategy as
+ configured on a resource which must comply with a few rules.
+3. An optional transformer, which can be used to manipulate the DSL state of
+ the entity and the resource.
+4. An optional verifier, which can be used to verify the DSL state of the
+ entity and the resource after compilation.
+5. The `AshAuthentication.Strategy` protocol, which provides the glue needed
+ for everything to wire up and wrappers around the actions needed to run on
+ the resource.
We're going to define an extremely dumb strategy which lets anyone with a name
-that starts with "Marty" sign in with just their name. Of course you would
+that starts with "Marty" sign in with just their name. Of course you would
never do this in real life, but this isn't real life - it's documentation!
## DSL setup
-Let's start by defining a module for our strategy to live in. Let's call it
+Let's start by defining a module for our strategy to live in. Let's call it
`OnlyMartiesAtTheParty`:
```elixir
@@ -36,7 +36,7 @@ defmodule OnlyMartiesAtTheParty do
end
```
-Sadly, this isn't enough to make the magic happen. We need to define our DSL
+Sadly, this isn't enough to make the magic happen. We need to define our DSL
entity by adding it to the `use` statement:
```elixir
@@ -87,22 +87,22 @@ end
If you haven't you should take a look at the docs for `Spark.Dsl.Entity`, but
here's a brief overview of what each field we've set does:
- - `name` is the name for which the helper function will be generated in
- the DSL (ie `only_marty do #... end`).
- - `describe` and `examples` are used when generating documentation.
- - `target` is the name of the module which defines our entity struct. We've
- set it to `__MODULE__` which means that we'll have to define the struct on
- this module.
- - `schema` is a keyword list that defines a `NimbleOptions` schema. Spark
- provides a number of additional types over the default ones though, so check
- out `Spark.OptionsHelpers` for more information.
+- `name` is the name for which the helper function will be generated in
+ the DSL (ie `only_marty do #... end`).
+- `describe` and `examples` are used when generating documentation.
+- `target` is the name of the module which defines our entity struct. We've
+ set it to `__MODULE__` which means that we'll have to define the struct on
+ this module.
+- `schema` is a keyword list that defines a `NimbleOptions` schema. Spark
+ provides a number of additional types over the default ones though, so check
+ out `Spark.OptionsHelpers` for more information.
> By default the entity is added to the `authentication / strategy` DSL, however
> if you want it in the `authentication / add_ons` DSL instead you can also pass
> `style: :add_on` in the `use` statement.
-Next up, we need to define our struct. The struct should have *at least* the
-fields named in the entity schema. Additionally, Ash Authentication requires
+Next up, we need to define our struct. The struct should have _at least_ the
+fields named in the entity schema. Additionally, Ash Authentication requires
that it have a `resource` field which will be set to the module of the resource
it's attached to during compilation.
@@ -123,11 +123,11 @@ by adding it to the `extensions` section of your resource:
```elixir
defmodule MyApp.Accounts.User do
- use Ash.Resource, extensions: [AshAuthentication, OnlyMartiesAtTheParty]
+ use Ash.Resource,
+ extensions: [AshAuthentication, OnlyMartiesAtTheParty],
+ domain: MyApp.Accounts
authentication do
- api MyApp.Accounts
-
strategies do
only_marty do
name_field :name
@@ -145,25 +145,25 @@ end
## Implementing the `AshAuthentication.Strategy` protocol
The Strategy protocol is used to introspect the strategy so that it can
-seamlessly fit in with the rest of Ash Authentication. Here are the key
+seamlessly fit in with the rest of Ash Authentication. Here are the key
concepts:
- - "phases" - in terms of HTTP, each strategy is likely to have many phases (eg
- OAuth 2.0's "request" and "callback" phases). Essentially you need one
- phase for each HTTP endpoint you wish to support with your strategy. In our
- case we just want one sign in endpoint.
- - "actions" - actions are exactly as they sound - Resource actions which can
- be executed by the strategy, whether generated by the strategy (as in the
- password strategy) or typed in by the user (as in the OAuth 2.0 strategy).
- The reason that we wrap the strategy's actions this way is that all the
- built-in strategies (and we hope yours too) allow the user to customise the
- name of the actions that it uses. At the very least it should probably
- append the strategy name to the action. Using `Strategy.action/4` allows us
- to refer these by a more generic name rather than via the user-specified one
- (eg `:register` vs `:register_with_password`).
- - "routes" - `AshAuthentication.Plug` (or `AshAuthentication.Phoenix.Router`)
- will generate routes using `Plug.Router` (or `Phoenix.Router`) - the
- `routes/1` callback is used to retrieve this information from the strategy.
+- "phases" - in terms of HTTP, each strategy is likely to have many phases (eg
+ OAuth 2.0's "request" and "callback" phases). Essentially you need one
+ phase for each HTTP endpoint you wish to support with your strategy. In our
+ case we just want one sign in endpoint.
+- "actions" - actions are exactly as they sound - Resource actions which can
+ be executed by the strategy, whether generated by the strategy (as in the
+ password strategy) or typed in by the user (as in the OAuth 2.0 strategy).
+ The reason that we wrap the strategy's actions this way is that all the
+ built-in strategies (and we hope yours too) allow the user to customise the
+ name of the actions that it uses. At the very least it should probably
+ append the strategy name to the action. Using `Strategy.action/4` allows us
+ to refer these by a more generic name rather than via the user-specified one
+ (eg `:register` vs `:register_with_password`).
+- "routes" - `AshAuthentication.Plug` (or `AshAuthentication.Phoenix.Router`)
+ will generate routes using `Plug.Router` (or `Phoenix.Router`) - the
+ `routes/1` callback is used to retrieve this information from the strategy.
Given this information, let's implement the strategy. It's quite long, so I'm
going to break it up into smaller chunks.
@@ -172,8 +172,8 @@ going to break it up into smaller chunks.
defimpl AshAuthentication.Strategy, for: OnlyMartiesAtTheParty do
```
-The `name/1` function is used to uniquely identify the strategy. It *must* be an
-atom and *should* be the same as the path fragment used in the generated routes.
+The `name/1` function is used to uniquely identify the strategy. It _must_ be an
+atom and _should_ be the same as the path fragment used in the generated routes.
```elixir
def name(strategy), do: strategy.name
@@ -187,7 +187,7 @@ and action.
def actions(_), do: [:sign_in]
```
-Next we generate the routes for the strategy. Routes *should* contain the
+Next we generate the routes for the strategy. Routes _should_ contain the
subject name of the resource being authenticated in case the implementer is
authenticating multiple different resources - eg `User` and `Admin`.
@@ -207,8 +207,8 @@ When generating routes or forms for this phase, what HTTP method should we use?
def method_for_phase(_, :sign_in), do: :post
```
-Next up, we write our plug. We take the "name field" from the input params in
-the conn and pass them to our sign in action. As long as the action returns
+Next up, we write our plug. We take the "name field" from the input params in
+the conn and pass them to our sign in action. As long as the action returns
`{:ok, Ash.Resource.record}` or `{:error, any}` then we can just pass it
straight into `store_authentication_result/2` from
`AshAuthentication.Plug.Helpers`.
@@ -223,11 +223,11 @@ straight into `store_authentication_result/2` from
end
```
-Finally, we implement our sign in action. We use `Ash.Query` to find all
+Finally, we implement our sign in action. We use `Ash.Query` to find all
records whose name field matches the input, then constrain it to only records
-whose name field starts with "Marty". Depending on whether the name field has a
+whose name field starts with "Marty". Depending on whether the name field has a
unique identity on it we have to deal with it returning zero or more users, or
-an error. When it returns a single user we return that user in an ok tuple,
+an error. When it returns a single user we return that user in an ok tuple,
otherwise we return an authentication failure.
In this example we're assuming that there is a default `read` action present on
@@ -246,22 +246,23 @@ the resource.
```elixir
alias AshAuthentication.Errors.AuthenticationFailed
require Ash.Query
+ import Ash.Expr
def action(strategy, :sign_in, params, options) do
name_field = strategy.name_field
name = Map.get(params, to_string(name_field))
- api = AshAuthentication.Info.authentication_api!(strategy.resource)
+ domain = AshAuthentication.Info.domain!(strategy.resource)
strategy.resource
- |> Ash.Query.filter(ref(^name_field) == ^name)
+ |> Ash.Query.filter(expr(^ref(name_field) == ^name))
|> then(fn query ->
if strategy.case_sensitive? do
- Ash.Query.filter(query, like(ref(^name_field), "Marty%"))
+ Ash.Query.filter(query, like(^ref(name_field), "Marty%"))
else
- Ash.Query.filter(query, ilike(ref(^name_field), "Marty%"))
+ Ash.Query.filter(query, ilike(^ref(name_field), "Marty%"))
end
end)
- |> api.read(options)
+ |> domain.read(options)
|> case do
{:ok, [user]} ->
{:ok, user}
@@ -282,7 +283,7 @@ end
## Bonus round - transformers and verifiers
In some cases it may be required for your strategy to modify it's own
-configuration or that of the whole resource at compile time. For that you can
+configuration or that of the whole resource at compile time. For that you can
define the `transform/2` callback on your strategy module.
At the very least it is good practice to call
@@ -294,7 +295,7 @@ imported by `use AshAuthentication.Strategy.Custom` for this purpose.
### Transformers
For simple cases where you're just transforming the strategy you can just return
-the modified strategy and the DSL will be updated accordingly. For example if
+the modified strategy and the DSL will be updated accordingly. For example if
you wanted to generate the name of an action if the user hasn't specified it:
```elixir
@@ -303,9 +304,9 @@ def transform(strategy, _dsl_state) do
end
```
-In some cases you may want to modify the strategy and the resources DSL. In
+In some cases you may want to modify the strategy and the resources DSL. In
this case you can return the newly mutated DSL state in an ok tuple or an error
-tuple, preferably containing a `Spark.Error.DslError`. For example if we wanted
+tuple, preferably containing a `Spark.Error.DslError`. For example if we wanted
to build a sign in action for `OnlyMartiesAtTheParty` to use:
```elixir
@@ -332,12 +333,12 @@ end
```
Transformers can also be used to validate user input or even directly add code
-to the resource. See the docs for `Spark.Dsl.Transformer` for more information.
+to the resource. See the docs for `Spark.Dsl.Transformer` for more information.
### Verifiers
We also support a variant of transformers which run in the new `@after_verify`
-compile hook provided by Elixir 1.14. This is a great place to put checks
+compile hook provided by Elixir 1.14. This is a great place to put checks
to make sure that the user's configuration makes sense without adding any
compile-time dependencies between modules which may cause compiler deadlocks.
diff --git a/documentation/tutorials/auth0-quickstart.md b/documentation/tutorials/auth0-quickstart.md
index 7eeba1d..f66db5c 100644
--- a/documentation/tutorials/auth0-quickstart.md
+++ b/documentation/tutorials/auth0-quickstart.md
@@ -6,41 +6,45 @@ Auth0 for authentication.
Before you start this tutorial, skip the Token resource while following the
[AshAuthenticationPhoenix guide](https://hexdocs.pm/ash_authentication_phoenix/getting-started-with-ash-authentication-phoenix.html))
-> [!WARNING]
+> [!WARNING]
> Make sure that your `ash_postgres` dependency is `~> 1.3.64`. A bug in previous versions prevents the action shown below from working correctly.
Next, you need to configure an application in [the Auth0
dashboard](https://manage.auth0.com/) using the following steps:
- 1. Click "Create Application".
- 2. Set your application name to something that identifies it. You will likely
- need separate applications for development and production environments, so
- keep that in mind.
- 3. Select "Regular Web Application" and click "Create".
- 4. Switch to the "Settings" tab.
- 5. Copy the "Domain", "Client ID" and "Client Secret" somewhere safe - we'll
- need them soon.
- 6. In the "Allowed Callback URLs" section, add your callback URL. The
- callback URL is generated from the following information:
- - The base URL of the application - in development that would be
- `http://localhost:4000/` but in production will be your application's
- URL.
- - The mount point of the auth routes in your router - we'll assume
- `/auth`.
- - The "subject name" of the resource being authenticated - we'll assume `user`.
- - The name of the strategy in your configuration. By default this is
- `auth0`.
+1. Click "Create Application".
+2. Set your application name to something that identifies it. You will likely
+ need separate applications for development and production environments, so
+ keep that in mind.
+3. Select "Regular Web Application" and click "Create".
+4. Switch to the "Settings" tab.
+5. Copy the "Domain", "Client ID" and "Client Secret" somewhere safe - we'll
+ need them soon.
+6. In the "Allowed Callback URLs" section, add your callback URL. The
+ callback URL is generated from the following information:
- This means that the callback URL should look something like
- `http://localhost:4000/auth/user/auth0/callback`.
- 7. Set "Allowed Web Origins" to your application's base URL.
- 8. Click "Save Changes".
+ - The base URL of the application - in development that would be
+ `http://localhost:4000/` but in production will be your application's
+ URL.
+ - The mount point of the auth routes in your router - we'll assume
+ `/auth`.
+ - The "subject name" of the resource being authenticated - we'll assume `user`.
+ - The name of the strategy in your configuration. By default this is
+ `auth0`.
+
+ This means that the callback URL should look something like
+ `http://localhost:4000/auth/user/auth0/callback`.
+
+7. Set "Allowed Web Origins" to your application's base URL.
+8. Click "Save Changes".
Next we can configure our resource:
```elixir
defmodule MyApp.Accounts.User do
- use Ash.Resource, extensions: [AshAuthentication]
+ use Ash.Resource,
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
authentication do
strategies do
@@ -92,29 +96,32 @@ end
The values for this configuration should be:
- * `client_id` - the client ID copied from the Auth0 settings page.
- * `redirect_uri` - the URL to the generated auth routes in your application
- (eg `http://localhost:4000/auth`).
- * `client_secret` the client secret copied from the Auth0 settings page.
- * `base_url` - the "domain" value copied from the Auth0 settings page prefixed
- with `https://` (eg `https://dev-yu30yo5y4tg2hg0y.us.auth0.com`).
+- `client_id` - the client ID copied from the Auth0 settings page.
+- `redirect_uri` - the URL to the generated auth routes in your application
+ (eg `http://localhost:4000/auth`).
+- `client_secret` the client secret copied from the Auth0 settings page.
+- `base_url` - the "domain" value copied from the Auth0 settings page prefixed
+ with `https://` (eg `https://dev-yu30yo5y4tg2hg0y.us.auth0.com`).
-Lastly, we need to add a register action to your user resource. This is defined
+Lastly, we need to add a register action to your user resource. This is defined
as an upsert so that it can register new users, or update information for
-returning users. The default name of the action is `register_with_` followed by
-the strategy name. In our case that is `register_with_auth0`.
+returning users. The default name of the action is `register_with_` followed by
+the strategy name. In our case that is `register_with_auth0`.
The register action takes two arguments, `user_info` and the `oauth_tokens`.
- - `user_info` contains the [`GET /userinfo` response from
- Auth0](https://auth0.com/docs/api/authentication#get-user-info) which you
- can use to populate your user attributes as needed.
- - `oauth_tokens` contains the [`POST /oauth/token` response from
- Auth0](https://auth0.com/docs/api/authentication#get-token) - you may want
- to store these if you intend to call the Auth0 API on behalf of the user.
+
+- `user_info` contains the [`GET /userinfo` response from
+ Auth0](https://auth0.com/docs/api/authentication#get-user-info) which you
+ can use to populate your user attributes as needed.
+- `oauth_tokens` contains the [`POST /oauth/token` response from
+ Auth0](https://auth0.com/docs/api/authentication#get-token) - you may want
+ to store these if you intend to call the Auth0 API on behalf of the user.
```elixir
defmodule MyApp.Accounts.User do
- use Ash.Resource, extensions: [AshAuthentication]
+ use Ash.Resource,
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
# ...
diff --git a/documentation/tutorials/getting-started-with-authentication.md b/documentation/tutorials/getting-started-with-authentication.md
index 038b2fc..b52e2f2 100644
--- a/documentation/tutorials/getting-started-with-authentication.md
+++ b/documentation/tutorials/getting-started-with-authentication.md
@@ -49,8 +49,8 @@ used when converting tokens or session information into a resource record.
### `AshAuthentication.Strategy.Password`
This authentication strategy provides registration and sign-in for users using a local
-identifier (eg `username`, `email` or `phone_number`) and a password. It will
-define register and sign-in actions on your "user" resource. You are welcome to
+identifier (eg `username`, `email` or `phone_number`) and a password. It will
+define register and sign-in actions on your "user" resource. You are welcome to
define either or both of these actions yourself if you wish to customise them -
if you do so then the extension will do it's best to validate that all required
configuration is present.
@@ -60,7 +60,7 @@ The `AshAuthentication.Strategy.Password` DSL allows you to override any of the
### `AshAuthentication.Strategy.OAuth2`
This authentication strategy provides registration and sign-in for users using a
-remote [OAuth 2.0](https://oauth.net/2/) server as the source of truth. You
+remote [OAuth 2.0](https://oauth.net/2/) server as the source of truth. You
will be required to provide either a "register" or a "sign-in" action depending
on your configuration, which the strategy will attempt to validate for common
misconfigurations.
@@ -74,7 +74,7 @@ change to take place.
### `AshAuthentication.TokenResource`
This extension allows you to easily create a resource which will store
-information about tokens that can't be encoded into the tokens themselves. A
+information about tokens that can't be encoded into the tokens themselves. A
resource with this extension must be present if token generation is enabled.
### `AshAuthentication.UserIdentity`
@@ -82,21 +82,21 @@ resource with this extension must be present if token generation is enabled.
If you plan to support multiple different strategies at once (eg giving your
users the choice of more than one authentication provider, or signing them into
multiple services simultaneously) then you will want to create a resource with
-this extension enabled. It is used to keep track of the links between your
+this extension enabled. It is used to keep track of the links between your
local user records and their many remote identities.
## Example
-Let's create an `Accounts` API in our application which provides a `User`
+Let's create an `Accounts` domain in our application which provides a `User`
resource and a `Token` resource.
-First, let's define our API:
+First, let's define our domain:
```elixir
# lib/my_app/accounts.ex
defmodule MyApp.Accounts do
- use Ash.Api
+ use Ash.Domain
resources do
resource MyApp.Accounts.User
@@ -105,15 +105,15 @@ defmodule MyApp.Accounts do
end
```
-Be sure to add it to the `ash_apis` config in your `config.exs`
+Be sure to add it to the `ash_domains` config in your `config.exs`
```elixir
# in config/config.exs
-config :my_app, :ash_apis: [..., MyApp.Accounts]
+config :my_app, :ash_domains: [..., MyApp.Accounts]
```
-Next, let's define our `Token` resource. This resource is needed
-if token generation is enabled for any resources in your application. Most of
+Next, let's define our `Token` resource. This resource is needed
+if token generation is enabled for any resources in your application. Most of
the contents are auto-generated, so we just need to provide the data layer
configuration and the API to use.
@@ -124,7 +124,7 @@ But before we do, we need to install a postgres extension.
defmodule MyApp.Repo do
use AshPostgres.Repo, otp_app: :my_app
-
+
def installed_extensions do
["uuid-ossp", "citext"]
end
@@ -140,11 +140,8 @@ You can skip this step if you don't want to use tokens, in which case remove the
defmodule MyApp.Accounts.Token do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
- extensions: [AshAuthentication.TokenResource]
-
- token do
- api MyApp.Accounts
- end
+ extensions: [AshAuthentication.TokenResource],
+ domain: MyApp.Accounts
postgres do
table "tokens"
@@ -170,17 +167,16 @@ defmodule MyApp.Accounts.User do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
extensions: [AshAuthentication],
- authorizers: [Ash.Policy.Authorizer]
+ authorizers: [Ash.Policy.Authorizer],
+ domain: MyApp.Accounts
attributes do
uuid_primary_key :id
- attribute :email, :ci_string, allow_nil?: false
- attribute :hashed_password, :string, allow_nil?: false, sensitive?: true, private?: true
+ attribute :email, :ci_string, allow_nil?: false, public?: true
+ attribute :hashed_password, :string, allow_nil?: false, sensitive?: true
end
authentication do
- api MyApp.Accounts
-
strategies do
password :password do
identity_field :email
@@ -231,8 +227,8 @@ If you're using Phoenix, then you can skip this section and go straight to
[Integrating Ash Authentication and Phoenix](https://ash-hq.org/docs/guides/ash_authentication_phoenix/latest/tutorials/getting-started-with-ash-authentication-phoenix)
In order for your users to be able to sign in, you will likely need to provide
-an HTTP endpoint to submit credentials or OAuth requests to. Ash Authentication
-provides `AshAuthentication.Plug` for this purposes. It provides a `use` macro
+an HTTP endpoint to submit credentials or OAuth requests to. Ash Authentication
+provides `AshAuthentication.Plug` for this purposes. It provides a `use` macro
which handles routing of requests to the correct providers, and defines
callbacks for successful and unsuccessful outcomes.
@@ -290,7 +286,7 @@ based on the contents of the session store or `Authorization` header.
## Supervisor
AshAuthentication includes a supervisor which you should add to your
-application's supervisor tree. This is used to run any periodic jobs related to
+application's supervisor tree. This is used to run any periodic jobs related to
your authenticated resources (removing expired tokens, for example).
### Example
@@ -314,9 +310,9 @@ end
## Token generation
If you have token generation enabled then you need to provide (at minimum) a
-signing secret. As the name implies this should be a secret. AshAuthentication
+signing secret. As the name implies this should be a secret. AshAuthentication
provides a mechanism for looking up secrets at runtime using the
-`AshAuthentication.Secret` behaviour. To save you a click, this means that you
+`AshAuthentication.Secret` behaviour. To save you a click, this means that you
can set your token signing secret using either a static string (please don't!),
a two-arity anonymous function, or a module which implements the
`AshAuthentication.Secret` behaviour.
diff --git a/documentation/tutorials/github-quickstart.md b/documentation/tutorials/github-quickstart.md
index 6da0498..6edd913 100644
--- a/documentation/tutorials/github-quickstart.md
+++ b/documentation/tutorials/github-quickstart.md
@@ -6,37 +6,41 @@ GitHub for authentication.
First you need to configure an application in your [GitHub developer
settings](https://github.com/settings/developers):
- 1. Click the "New OAuth App" button.
- 2. Set your application name to something that identifies it. You will likely
- need separate applications for development and production environments, so
- keep that in mind.
- 3. Set "Homepage URL" appropriately for your application and environment.
- 4. In the "Authorization callback URL" section, add your callback URL. The
- callback URL is generated from the following information:
- - The base URL of the application - in development that would be
- `http://localhost:4000/` but in production will be your application's
- URL.
- - The mount point of the auth routes in your router - we'll assume
- `/auth`.
- - The "subject name" of the resource being authenticated - we'll assume `user`.
- - The name of the strategy in your configuration. By default this is
- `github`.
+1. Click the "New OAuth App" button.
+2. Set your application name to something that identifies it. You will likely
+ need separate applications for development and production environments, so
+ keep that in mind.
+3. Set "Homepage URL" appropriately for your application and environment.
+4. In the "Authorization callback URL" section, add your callback URL. The
+ callback URL is generated from the following information:
- This means that the callback URL should look something like
- `http://localhost:4000/auth/user/github/callback`.
- 5. Do not set "Enable Device Flow" unless you know why you want this.
- 6. Click "Register application".
- 7. Click "Generate a new client secret".
- 8. Copy the "Client ID" and "Client secret" somewhere safe, we'll need them
- soon.
- 9. Click "Update application".
+ - The base URL of the application - in development that would be
+ `http://localhost:4000/` but in production will be your application's
+ URL.
+ - The mount point of the auth routes in your router - we'll assume
+ `/auth`.
+ - The "subject name" of the resource being authenticated - we'll assume `user`.
+ - The name of the strategy in your configuration. By default this is
+ `github`.
+
+ This means that the callback URL should look something like
+ `http://localhost:4000/auth/user/github/callback`.
+
+5. Do not set "Enable Device Flow" unless you know why you want this.
+6. Click "Register application".
+7. Click "Generate a new client secret".
+8. Copy the "Client ID" and "Client secret" somewhere safe, we'll need them
+ soon.
+9. Click "Update application".
Next we can configure our resource (assuming you already have everything else
set up):
```elixir
defmodule MyApp.Accounts.User do
- use Ash.Resource, extensions: [AshAuthentication]
+ use Ash.Resource,
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
authentication do
strategies do
@@ -82,28 +86,31 @@ end
The values for this configuration should be:
- * `client_id` - the client ID copied from the GitHub settings page.
- * `redirect_uri` - the URL to the generated auth routes in your application
- (eg `http://localhost:4000/auth`).
- * `client_secret` the client secret copied from the GitHub settings page.
+- `client_id` - the client ID copied from the GitHub settings page.
+- `redirect_uri` - the URL to the generated auth routes in your application
+ (eg `http://localhost:4000/auth`).
+- `client_secret` the client secret copied from the GitHub settings page.
-Lastly, we need to add a register action to your user resource. This is defined
+Lastly, we need to add a register action to your user resource. This is defined
as an upsert so that it can register new users, or update information for
-returning users. The default name of the action is `register_with_` followed by
-the strategy name. In our case that is `register_with_github`.
+returning users. The default name of the action is `register_with_` followed by
+the strategy name. In our case that is `register_with_github`.
The register action takes two arguments, `user_info` and the `oauth_tokens`.
- - `user_info` contains the [`GET /user` response from
- GitHub](https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user)
- which you can use to populate your user attributes as needed.
- - `oauth_tokens` contains the [`POST /login/oauth/access_token` response from
- GitHub](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#response)
- - you may want to store these if you intend to call the GitHub API on behalf
+
+- `user_info` contains the [`GET /user` response from
+ GitHub](https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user)
+ which you can use to populate your user attributes as needed.
+- `oauth_tokens` contains the [`POST /login/oauth/access_token` response from
+ GitHub](https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#response)
+ - you may want to store these if you intend to call the GitHub API on behalf
of the user.
```elixir
defmodule MyApp.Accounts.User do
- use Ash.Resource, extensions: [AshAuthentication]
+ use Ash.Resource,
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
# ...
diff --git a/documentation/tutorials/google-quickstart.md b/documentation/tutorials/google-quickstart.md
index 95e9353..879bd80 100644
--- a/documentation/tutorials/google-quickstart.md
+++ b/documentation/tutorials/google-quickstart.md
@@ -10,9 +10,11 @@ First you'll need a registered application in [Google Cloud](https://console.clo
Next we configure our resource to use google credentials:
-``` elixir
+```elixir
defmodule MyApp.Accounts.User do
- use Ash.Resource, extensions: [AshAuthentication]
+ use Ash.Resource,
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
attributes do
...
@@ -34,9 +36,12 @@ end
Please check the guide on how to properly configure your Secrets
Then we need to define an action that will handle the oauth2 flow, for the google case it is `:register_with_google` it will handle both cases for our resource, user registration & login.
-``` elixir
+```elixir
defmodule MyApp.Accounts.User do
- use Ash.Resource, extensions: [AshAuthentication]
+ use Ash.Resource,
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
+
# ...
actions do
create :register_with_google do
diff --git a/lib/ash_authentication.ex b/lib/ash_authentication.ex
index 5cf5324..511ddf3 100644
--- a/lib/ash_authentication.ex
+++ b/lib/ash_authentication.ex
@@ -16,7 +16,8 @@ defmodule AshAuthentication do
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
- extensions: [AshAuthentication]
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
attributes do
uuid_primary_key :id
@@ -25,8 +26,6 @@ defmodule AshAuthentication do
end
authentication do
- api MyApp.Accounts
-
strategies do
password :password do
identity_field :email
@@ -83,7 +82,7 @@ defmodule AshAuthentication do
for more information.
"""
alias Ash.{
- Api,
+ Domain,
Error.Query.NotFound,
Query,
Resource
@@ -120,7 +119,7 @@ defmodule AshAuthentication do
require Ash.Query
@type resource_config :: %{
- api: module,
+ domain: module,
providers: [module],
resource: module,
subject_name: atom
@@ -145,8 +144,8 @@ defmodule AshAuthentication do
|> List.wrap()
|> Enum.flat_map(fn otp_app ->
otp_app
- |> Application.get_env(:ash_apis, [])
- |> Stream.flat_map(&Api.Info.resources(&1))
+ |> Application.get_env(:ash_domains, [])
+ |> Stream.flat_map(&Domain.Info.resources(&1))
|> Stream.uniq()
|> Stream.filter(&(AshAuthentication in Spark.extensions(&1)))
|> Enum.to_list()
@@ -185,7 +184,7 @@ defmodule AshAuthentication do
iex> %{id: user_id} = build_user()
...> {:ok, %{id: ^user_id}} = subject_to_user("user?id=#{user_id}", Example.User)
- Any options passed will be passed to the underlying `Api.read/2` callback.
+ Any options passed will be passed to the underlying `Domain.read/2` callback.
"""
@spec subject_to_user(subject | URI.t(), Resource.t(), keyword) ::
{:ok, Resource.record()} | {:error, any}
@@ -199,7 +198,7 @@ defmodule AshAuthentication do
with {:ok, resource_subject_name} <- Info.authentication_subject_name(resource),
^subject_name <- to_string(resource_subject_name),
{:ok, action_name} <- Info.authentication_get_by_subject_action_name(resource),
- {:ok, api} <- Info.authentication_api(resource) do
+ {:ok, domain} <- Info.domain(resource) do
primary_key =
primary_key
|> URI.decode_query()
@@ -214,7 +213,7 @@ defmodule AshAuthentication do
})
|> Query.for_read(action_name, %{})
|> Query.filter(^primary_key)
- |> api.read(options)
+ |> domain.read(options)
|> case do
{:ok, [user]} -> {:ok, user}
_ -> {:error, NotFound.exception([])}
diff --git a/lib/ash_authentication/add_ons/confirmation.ex b/lib/ash_authentication/add_ons/confirmation.ex
index d341cf6..5a05dd3 100644
--- a/lib/ash_authentication/add_ons/confirmation.ex
+++ b/lib/ash_authentication/add_ons/confirmation.ex
@@ -21,7 +21,8 @@ defmodule AshAuthentication.AddOn.Confirmation do
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
- extensions: [AshAuthentication]
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
attributes do
uuid_primary_key :id
@@ -29,8 +30,6 @@ defmodule AshAuthentication.AddOn.Confirmation do
end
authentication do
- api MyApp.Accounts
-
add_ons do
confirmation :confirm do
monitor_fields [:email]
diff --git a/lib/ash_authentication/add_ons/confirmation/actions.ex b/lib/ash_authentication/add_ons/confirmation/actions.ex
index 1a58a75..8df33df 100644
--- a/lib/ash_authentication/add_ons/confirmation/actions.ex
+++ b/lib/ash_authentication/add_ons/confirmation/actions.ex
@@ -21,7 +21,7 @@ defmodule AshAuthentication.AddOn.Confirmation.Actions do
"""
@spec confirm(Confirmation.t(), map, keyword) :: {:ok, Resource.record()} | {:error, any}
def confirm(strategy, params, opts \\ []) do
- with {:ok, api} <- Info.authentication_api(strategy.resource),
+ with {:ok, domain} <- Info.domain(strategy.resource),
{:ok, token} <- Map.fetch(params, "confirm"),
{:ok, %{"sub" => subject}, _} <- Jwt.verify(token, strategy.resource),
{:ok, user} <- AshAuthentication.subject_to_user(subject, strategy.resource, opts) do
@@ -33,7 +33,7 @@ defmodule AshAuthentication.AddOn.Confirmation.Actions do
}
})
|> Changeset.for_update(strategy.confirm_action_name, params)
- |> api.update(opts)
+ |> domain.update(opts)
else
:error -> {:error, InvalidToken.exception(type: :confirmation)}
{:error, reason} -> {:error, reason}
@@ -52,7 +52,7 @@ defmodule AshAuthentication.AddOn.Confirmation.Actions do
|> Map.new()
with {:ok, token_resource} <- Info.authentication_tokens_token_resource(strategy.resource),
- {:ok, api} <- TokenResource.Info.token_api(token_resource),
+ {:ok, domain} <- TokenResource.Info.token_domain(token_resource),
{:ok, store_changes_action} <-
TokenResource.Info.token_confirmation_store_changes_action_name(token_resource),
{:ok, _token_record} <-
@@ -68,7 +68,7 @@ defmodule AshAuthentication.AddOn.Confirmation.Actions do
extra_data: changes,
purpose: to_string(Strategy.name(strategy))
})
- |> api.create(Keyword.merge(opts, upsert?: true)) do
+ |> domain.create(Keyword.merge(opts, upsert?: true)) do
:ok
else
{:error, reason} ->
@@ -88,7 +88,7 @@ defmodule AshAuthentication.AddOn.Confirmation.Actions do
@spec get_changes(Confirmation.t(), String.t(), keyword) :: {:ok, map} | :error
def get_changes(strategy, jti, opts \\ []) do
with {:ok, token_resource} <- Info.authentication_tokens_token_resource(strategy.resource),
- {:ok, api} <- TokenResource.Info.token_api(token_resource),
+ {:ok, domain} <- TokenResource.Info.token_domain(token_resource),
{:ok, get_changes_action} <-
TokenResource.Info.token_confirmation_get_changes_action_name(token_resource),
{:ok, [token_record]} <-
@@ -101,7 +101,7 @@ defmodule AshAuthentication.AddOn.Confirmation.Actions do
})
|> Query.set_context(%{strategy: strategy})
|> Query.for_read(get_changes_action, %{"jti" => jti})
- |> api.read(opts) do
+ |> domain.read(opts) do
changes =
strategy.monitor_fields
|> Stream.map(&to_string/1)
diff --git a/lib/ash_authentication/add_ons/confirmation/confirm_change.ex b/lib/ash_authentication/add_ons/confirmation/confirm_change.ex
index 1e2a0ce..5850f32 100644
--- a/lib/ash_authentication/add_ons/confirmation/confirm_change.ex
+++ b/lib/ash_authentication/add_ons/confirmation/confirm_change.ex
@@ -29,6 +29,11 @@ defmodule AshAuthentication.AddOn.Confirmation.ConfirmChange do
defp do_change(changeset, strategy) do
changeset
+ |> Changeset.set_context(%{
+ private: %{
+ ash_authentication?: true
+ }
+ })
|> Changeset.before_action(fn changeset ->
with token when is_binary(token) <- Changeset.get_argument(changeset, :confirm),
{:ok, %{"act" => action, "jti" => jti}, _} <-
@@ -41,11 +46,14 @@ defmodule AshAuthentication.AddOn.Confirmation.ConfirmChange do
else: %{}
changeset
- |> Changeset.change_attributes(allowed_changes)
+ |> Changeset.force_change_attributes(allowed_changes)
|> Changeset.change_attribute(strategy.confirmed_at_field, DateTime.utc_now())
else
_ ->
- raise InvalidArgument, field: :confirm, message: "is not valid"
+ changeset
+ |> Changeset.add_error(
+ InvalidArgument.exception(field: :confirm, message: "is not valid")
+ )
end
end)
end
diff --git a/lib/ash_authentication/add_ons/confirmation/confirmation_hook_change.ex b/lib/ash_authentication/add_ons/confirmation/confirmation_hook_change.ex
index 9406d65..4dbbc2b 100644
--- a/lib/ash_authentication/add_ons/confirmation/confirmation_hook_change.ex
+++ b/lib/ash_authentication/add_ons/confirmation/confirmation_hook_change.ex
@@ -53,15 +53,18 @@ defmodule AshAuthentication.AddOn.Confirmation.ConfirmationHookChange do
defp do_change(changeset, strategy) do
changeset
- |> Changeset.before_action(fn changeset ->
- changeset
- |> not_confirm_action(strategy)
- |> should_confirm_action_type(strategy)
- |> monitored_field_changing(strategy)
- |> changes_would_be_valid()
- |> maybe_inhibit_updates(strategy)
- |> maybe_perform_confirmation(strategy, changeset)
- end)
+ |> Changeset.before_action(
+ fn changeset ->
+ changeset
+ |> not_confirm_action(strategy)
+ |> should_confirm_action_type(strategy)
+ |> monitored_field_changing(strategy)
+ |> changes_would_be_valid()
+ |> maybe_inhibit_updates(strategy)
+ |> maybe_perform_confirmation(strategy, changeset)
+ end,
+ prepend?: true
+ )
end
defp not_confirm_action(%Changeset{} = changeset, strategy)
diff --git a/lib/ash_authentication/add_ons/confirmation/transformer.ex b/lib/ash_authentication/add_ons/confirmation/transformer.ex
index 0885721..94dd5db 100644
--- a/lib/ash_authentication/add_ons/confirmation/transformer.ex
+++ b/lib/ash_authentication/add_ons/confirmation/transformer.ex
@@ -83,6 +83,7 @@ defmodule AshAuthentication.AddOn.Confirmation.Transformer do
with {:ok, resource} <- persisted_option(dsl_state, :module),
{:ok, attribute} <- find_attribute(dsl_state, field),
:ok <- validate_attribute_option(attribute, resource, :writable?, [true]),
+ :ok <- validate_attribute_option(attribute, resource, :public?, [true]),
:ok <- maybe_validate_eager_checking(dsl_state, strategy, field, resource) do
{:cont, :ok}
else
@@ -142,7 +143,8 @@ defmodule AshAuthentication.AddOn.Confirmation.Transformer do
accept: strategy.monitor_fields,
arguments: arguments,
metadata: metadata,
- changes: changes
+ changes: changes,
+ require_atomic?: false
)
end
@@ -150,8 +152,26 @@ defmodule AshAuthentication.AddOn.Confirmation.Transformer do
with {:ok, action} <- validate_action_exists(dsl_state, strategy.confirm_action_name),
:ok <- validate_action_has_change(action, Confirmation.ConfirmChange),
:ok <- validate_action_argument_option(action, :confirm, :allow_nil?, [false]),
- :ok <- validate_action_has_change(action, GenerateTokenChange) do
- validate_action_argument_option(action, :confirm, :type, [Type.String])
+ :ok <- validate_action_argument_option(action, :confirm, :type, [Type.String]),
+ :ok <- validate_action_has_change(action, GenerateTokenChange),
+ :ok <- validate_action_option(action, :require_atomic?, [false]) do
+ accept_fields = MapSet.new(action.accept)
+
+ strategy.monitor_fields
+ |> MapSet.new()
+ |> MapSet.difference(accept_fields)
+ |> Enum.to_list()
+ |> case do
+ [] ->
+ :ok
+
+ _fields ->
+ {:error,
+ DslError.exception(
+ path: [:actions, action.name, :accept],
+ message: "The confirmation action must accept the monitored fields."
+ )}
+ end
end
end
diff --git a/lib/ash_authentication/dsl.ex b/lib/ash_authentication/dsl.ex
index 14b0df9..8a63614 100644
--- a/lib/ash_authentication/dsl.ex
+++ b/lib/ash_authentication/dsl.ex
@@ -8,7 +8,7 @@ defmodule AshAuthentication.Dsl do
import AshAuthentication.Utils, only: [to_sentence: 2]
import Joken.Signer, only: [algorithms: 0]
- alias Ash.{Api, Resource}
+ alias Ash.{Domain, Resource}
@default_token_lifetime_days 14
@@ -41,17 +41,18 @@ defmodule AshAuthentication.Dsl do
%Section{
name: :authentication,
describe: "Configure authentication for this resource",
- modules: [:api],
+ modules: [:domain],
schema: [
subject_name: [
type: :atom,
doc:
"The subject name is used anywhere that a short version of your resource name is needed. Must be unique system-wide and will be inferred from the resource name by default (ie `MyApp.Accounts.User` -> `user`)."
],
- api: [
- type: {:behaviour, Api},
+ domain: [
+ type: {:behaviour, Domain},
+ required: false,
doc:
- "The name of the Ash API to use to access this resource when doing anything authenticaiton related."
+ "The name of the Ash domain to use to access this resource when doing anything authentication related."
],
get_by_subject_action_name: [
type: :atom,
diff --git a/lib/ash_authentication/errors/authentication_failed.ex b/lib/ash_authentication/errors/authentication_failed.ex
index f5d2722..2aee39d 100644
--- a/lib/ash_authentication/errors/authentication_failed.ex
+++ b/lib/ash_authentication/errors/authentication_failed.ex
@@ -3,16 +3,20 @@ defmodule AshAuthentication.Errors.AuthenticationFailed do
A generic, authentication failed error.
"""
use Ash.Error.Exception
- def_ash_error([:field, :strategy, caused_by: %{}], class: :forbidden)
- import AshAuthentication.Debug
+
+ use Splode.Error,
+ fields: [
+ caused_by: %{},
+ changeset: nil,
+ field: nil,
+ query: nil,
+ strategy: nil
+ ],
+ class: :forbidden
@type t :: Exception.t()
- def exception(args) do
- args
- |> super()
- |> describe()
- end
+ def message(_), do: "Authentication failed"
defimpl Ash.ErrorKind do
@moduledoc false
diff --git a/lib/ash_authentication/errors/invalid_token.ex b/lib/ash_authentication/errors/invalid_token.ex
index d953463..30ff67b 100644
--- a/lib/ash_authentication/errors/invalid_token.ex
+++ b/lib/ash_authentication/errors/invalid_token.ex
@@ -3,7 +3,9 @@ defmodule AshAuthentication.Errors.InvalidToken do
An invalid token was presented.
"""
use Ash.Error.Exception
- def_ash_error([:type], class: :forbidden)
+ use Splode.Error, fields: [:type], class: :forbidden
+
+ def message(%{type: type}), do: "Invalid #{type} token"
defimpl Ash.ErrorKind do
@moduledoc false
diff --git a/lib/ash_authentication/errors/missing_secret.ex b/lib/ash_authentication/errors/missing_secret.ex
index 19bc30f..435c80b 100644
--- a/lib/ash_authentication/errors/missing_secret.ex
+++ b/lib/ash_authentication/errors/missing_secret.ex
@@ -3,7 +3,11 @@ defmodule AshAuthentication.Errors.MissingSecret do
A secret is now missing.
"""
use Ash.Error.Exception
- def_ash_error([:resource], class: :forbidden)
+ use Splode.Error, fields: [:resource], class: :forbidden
+
+ def message(%{path: path, resource: resource}) do
+ "Secret for `#{Enum.join(path, ".")}` on the `#{inspect(resource)}` resource is not accessible."
+ end
defimpl Ash.ErrorKind do
@moduledoc false
diff --git a/lib/ash_authentication/info.ex b/lib/ash_authentication/info.ex
index c50db52..93d8c94 100644
--- a/lib/ash_authentication/info.ex
+++ b/lib/ash_authentication/info.ex
@@ -7,7 +7,7 @@ defmodule AshAuthentication.Info do
extension: AshAuthentication,
sections: [:authentication]
- alias Ash.{Changeset, Query}
+ alias Ash.{Changeset, Domain, Query, Resource}
alias AshAuthentication.Strategy
alias Spark.Dsl.Extension
@@ -103,4 +103,33 @@ defmodule AshAuthentication.Info do
{:ok, strategy}
end
end
+
+ @doc """
+ Retrieve the domain to use for authentication.
+
+ If the `authentication.domain` DSL option is set, it will be used, otherwise
+ it will default to that configured on the resource.
+ """
+ @spec domain(dsl_or_resource) :: {:ok, Domain.t()} | :error
+ def domain(dsl_or_resource) do
+ auth_domain =
+ case authentication_domain(dsl_or_resource) do
+ {:ok, value} -> value
+ :error -> nil
+ end
+
+ resource_domain = Resource.Info.domain(dsl_or_resource)
+
+ domain = auth_domain || resource_domain
+
+ if domain, do: {:ok, domain}, else: :error
+ end
+
+ @doc "Raising version of `domain/1`"
+ def domain!(dsl_or_resource) do
+ case domain(dsl_or_resource) do
+ {:ok, value} -> value
+ :error -> raise "No `domain` configured on resource `#{inspect(dsl_or_resource)}`"
+ end
+ end
end
diff --git a/lib/ash_authentication/plug/macros.ex b/lib/ash_authentication/plug/macros.ex
index 10377a9..01013e3 100644
--- a/lib/ash_authentication/plug/macros.ex
+++ b/lib/ash_authentication/plug/macros.ex
@@ -3,7 +3,7 @@ defmodule AshAuthentication.Plug.Macros do
Generators used within `use AshAuthentication.Plug`.
"""
- alias Ash.Api
+ alias Ash.Domain
alias AshAuthentication.Plug.Helpers
alias Plug.Conn
alias Spark.Dsl.Extension
@@ -14,11 +14,9 @@ defmodule AshAuthentication.Plug.Macros do
@spec validate_subject_name_uniqueness(atom) :: Macro.t()
defmacro validate_subject_name_uniqueness(otp_app) do
quote do
- require Ash.Api.Info
-
unquote(otp_app)
- |> Application.compile_env(:ash_apis, [])
- |> Stream.flat_map(&Api.Info.depend_on_resources(&1))
+ |> Application.compile_env(:ash_domains, [])
+ |> Stream.flat_map(&Domain.Info.resources(&1))
|> Stream.map(&{&1, Extension.get_persisted(&1, :authentication)})
|> Stream.reject(&(elem(&1, 1) == nil))
|> Stream.map(&{elem(&1, 0), elem(&1, 1).subject_name})
diff --git a/lib/ash_authentication/plug/router.ex b/lib/ash_authentication/plug/router.ex
index 33bdcb9..b0d20c2 100644
--- a/lib/ash_authentication/plug/router.ex
+++ b/lib/ash_authentication/plug/router.ex
@@ -22,15 +22,14 @@ defmodule AshAuthentication.Plug.Router do
|> Macro.expand_once(__CALLER__)
quote do
- require Ash.Api.Info
use Plug.Router
plug(:match)
plug(:dispatch)
routes =
unquote(otp_app)
- |> Application.compile_env(:ash_apis, [])
- |> Stream.flat_map(&Ash.Api.Info.depend_on_resources(&1))
+ |> Application.compile_env(:ash_domains, [])
+ |> Stream.flat_map(&Ash.Domain.Info.resources(&1))
|> Stream.filter(&(AshAuthentication in Spark.extensions(&1)))
|> Stream.flat_map(&Info.authentication_strategies/1)
|> Stream.flat_map(fn strategy ->
diff --git a/lib/ash_authentication/secret.ex b/lib/ash_authentication/secret.ex
index abee2bc..61d3eac 100644
--- a/lib/ash_authentication/secret.ex
+++ b/lib/ash_authentication/secret.ex
@@ -15,11 +15,11 @@ defmodule AshAuthentication.Secret do
end
defmodule MyApp.Accounts.User do
- use Ash.Resource, extensions: [AshAuthentication]
+ use Ash.Resource,
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
authentication do
- api MyApp.Accounts
-
strategies do
oauth2 do
client_id MyApp.GetSecret
@@ -34,11 +34,11 @@ defmodule AshAuthentication.Secret do
```elixir
defmodule MyApp.User do
- use Ash.Resource, extensions: [AshAuthentication]
+ use Ash.Resource,
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
authentication do
- api MyApp.Accounts
-
strategies do
oauth2 do
client_id fn _secret, _resource ->
diff --git a/lib/ash_authentication/sender.ex b/lib/ash_authentication/sender.ex
index 031b4b6..d786f08 100644
--- a/lib/ash_authentication/sender.ex
+++ b/lib/ash_authentication/sender.ex
@@ -41,11 +41,11 @@ defmodule AshAuthentication.Sender do
end
defmodule MyApp.Accounts.User do
- use Ash.Resource, extensions: [AshAuthentication]
+ use Ash.Resource,
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
authentication do
- api MyApp.Accounts
-
strategies do
password :password do
resettable do
@@ -57,15 +57,15 @@ defmodule AshAuthentication.Sender do
end
```
- You can also implment it directly as a function:
+ You can also implement it directly as a function:
```elixir
defmodule MyApp.Accounts.User do
- use Ash.Resource, extensions: [AshAuthentication]
+ use Ash.Resource,
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
authentication do
- api MyApp.Accounts
-
strategies do
password :password do
resettable do
diff --git a/lib/ash_authentication/strategies/magic_link.ex b/lib/ash_authentication/strategies/magic_link.ex
index 2af8830..f1234fa 100644
--- a/lib/ash_authentication/strategies/magic_link.ex
+++ b/lib/ash_authentication/strategies/magic_link.ex
@@ -18,7 +18,8 @@ defmodule AshAuthentication.Strategy.MagicLink do
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
- extensions: [AshAuthentication]
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
attributes do
uuid_primary_key :id
@@ -26,8 +27,6 @@ defmodule AshAuthentication.Strategy.MagicLink do
end
authentication do
- api MyApp.Accounts
-
strategies do
magic_link do
identity_field :email
diff --git a/lib/ash_authentication/strategies/magic_link/actions.ex b/lib/ash_authentication/strategies/magic_link/actions.ex
index b21d9f2..186f379 100644
--- a/lib/ash_authentication/strategies/magic_link/actions.ex
+++ b/lib/ash_authentication/strategies/magic_link/actions.ex
@@ -14,13 +14,13 @@ defmodule AshAuthentication.Strategy.MagicLink.Actions do
"""
@spec request(MagicLink.t(), map, keyword) :: :ok | {:error, any}
def request(strategy, params, options) do
- api = Info.authentication_api!(strategy.resource)
+ domain = Info.domain!(strategy.resource)
strategy.resource
|> Query.new()
|> Query.set_context(%{private: %{ash_authentication?: true}})
|> Query.for_read(strategy.request_action_name, params)
- |> api.read(options)
+ |> domain.read(options)
|> case do
{:ok, _} -> :ok
{:error, reason} -> {:error, reason}
@@ -33,13 +33,13 @@ defmodule AshAuthentication.Strategy.MagicLink.Actions do
@spec sign_in(MagicLink.t(), map, keyword) ::
{:ok, Resource.record()} | {:error, Errors.AuthenticationFailed.t()}
def sign_in(strategy, params, options) do
- api = Info.authentication_api!(strategy.resource)
+ domain = Info.domain!(strategy.resource)
strategy.resource
|> Query.new()
|> Query.set_context(%{private: %{ash_authentication?: true}})
|> Query.for_read(strategy.sign_in_action_name, params)
- |> api.read(options)
+ |> domain.read(options)
|> case do
{:ok, [user]} ->
{:ok, user}
diff --git a/lib/ash_authentication/strategies/magic_link/request_preparation.ex b/lib/ash_authentication/strategies/magic_link/request_preparation.ex
index 400a99f..61c6167 100644
--- a/lib/ash_authentication/strategies/magic_link/request_preparation.ex
+++ b/lib/ash_authentication/strategies/magic_link/request_preparation.ex
@@ -18,7 +18,7 @@ defmodule AshAuthentication.Strategy.MagicLink.RequestPreparation do
@doc false
@impl true
- @spec prepare(Query.t(), keyword, Preparation.context()) :: Query.t()
+ @spec prepare(Query.t(), keyword, Preparation.Context.t()) :: Query.t()
def prepare(query, _opts, _context) do
strategy = Info.strategy_for_action!(query.resource, query.action.name)
@@ -27,7 +27,7 @@ defmodule AshAuthentication.Strategy.MagicLink.RequestPreparation do
select_for_senders = Info.authentication_select_for_senders!(query.resource)
query
- |> Query.filter(ref(^identity_field) == ^identity)
+ |> Query.filter(^ref(identity_field) == ^identity)
|> Query.before_action(fn query ->
Ash.Query.ensure_selected(query, select_for_senders)
end)
diff --git a/lib/ash_authentication/strategies/magic_link/sign_in_preparation.ex b/lib/ash_authentication/strategies/magic_link/sign_in_preparation.ex
index e0f30a7..b9cfa38 100644
--- a/lib/ash_authentication/strategies/magic_link/sign_in_preparation.ex
+++ b/lib/ash_authentication/strategies/magic_link/sign_in_preparation.ex
@@ -10,7 +10,7 @@ defmodule AshAuthentication.Strategy.MagicLink.SignInPreparation do
@doc false
@impl true
- @spec prepare(Query.t(), keyword, Preparation.context()) :: Query.t()
+ @spec prepare(Query.t(), keyword, Preparation.Context.t()) :: Query.t()
def prepare(query, _otps, _context) do
subject_name =
query.resource
diff --git a/lib/ash_authentication/strategies/oauth2.ex b/lib/ash_authentication/strategies/oauth2.ex
index 2f8ebe6..80deda9 100644
--- a/lib/ash_authentication/strategies/oauth2.ex
+++ b/lib/ash_authentication/strategies/oauth2.ex
@@ -19,7 +19,8 @@ defmodule AshAuthentication.Strategy.OAuth2 do
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
- extensions: [AshAuthentication]
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
attributes do
uuid_primary_key :id
@@ -27,8 +28,6 @@ defmodule AshAuthentication.Strategy.OAuth2 do
end
authentication do
- api MyApp.Accounts
-
strategies do
oauth2 :example do
client_id "OAuth Client ID"
@@ -157,8 +156,6 @@ defmodule AshAuthentication.Strategy.OAuth2 do
end
authentication do
- api MyApp.Accounts
-
strategies do
oauth2 :example do
registration_enabled? false
@@ -195,8 +192,6 @@ defmodule AshAuthentication.Strategy.OAuth2 do
end
authentication do
- api MyApp.Accounts
-
strategies do
oauth2 :example do
end
diff --git a/lib/ash_authentication/strategies/oauth2/actions.ex b/lib/ash_authentication/strategies/oauth2/actions.ex
index 8d3b8d2..2a99926 100644
--- a/lib/ash_authentication/strategies/oauth2/actions.ex
+++ b/lib/ash_authentication/strategies/oauth2/actions.ex
@@ -22,7 +22,7 @@ defmodule AshAuthentication.Strategy.OAuth2.Actions do
)}
def sign_in(%OAuth2{} = strategy, params, options) do
- api = Info.authentication_api!(strategy.resource)
+ domain = Info.domain!(strategy.resource)
strategy.resource
|> Query.new()
@@ -32,7 +32,7 @@ defmodule AshAuthentication.Strategy.OAuth2.Actions do
}
})
|> Query.for_read(strategy.sign_in_action_name, params)
- |> api.read(options)
+ |> domain.read(options)
|> case do
{:ok, [user]} ->
{:ok, user}
@@ -90,7 +90,7 @@ defmodule AshAuthentication.Strategy.OAuth2.Actions do
"""
@spec register(OAuth2.t(), map, keyword) :: {:ok, Resource.record()} | {:error, any}
def register(%OAuth2{} = strategy, params, options) when strategy.registration_enabled? do
- api = Info.authentication_api!(strategy.resource)
+ domain = Info.domain!(strategy.resource)
action = Resource.Info.action(strategy.resource, strategy.register_action_name, :create)
strategy.resource
@@ -104,7 +104,7 @@ defmodule AshAuthentication.Strategy.OAuth2.Actions do
upsert?: true,
upsert_identity: action.upsert_identity
)
- |> api.create(options)
+ |> domain.create(options)
end
def register(%OAuth2{} = strategy, _params, _options),
diff --git a/lib/ash_authentication/strategies/oauth2/identity_change.ex b/lib/ash_authentication/strategies/oauth2/identity_change.ex
index ea55ba0..b62c828 100644
--- a/lib/ash_authentication/strategies/oauth2/identity_change.ex
+++ b/lib/ash_authentication/strategies/oauth2/identity_change.ex
@@ -42,7 +42,7 @@ defmodule AshAuthentication.Strategy.OAuth2.IdentityChange do
"#{user_id_attribute_name}": user.id
}) do
user
- |> changeset.api.load(strategy.identity_relationship_name)
+ |> changeset.domain.load(strategy.identity_relationship_name)
else
:error -> :error
{:error, reason} -> {:error, reason}
diff --git a/lib/ash_authentication/strategies/oauth2/sign_in_preparation.ex b/lib/ash_authentication/strategies/oauth2/sign_in_preparation.ex
index ea1a01d..b369690 100644
--- a/lib/ash_authentication/strategies/oauth2/sign_in_preparation.ex
+++ b/lib/ash_authentication/strategies/oauth2/sign_in_preparation.ex
@@ -17,7 +17,7 @@ defmodule AshAuthentication.Strategy.OAuth2.SignInPreparation do
@doc false
@impl true
- @spec prepare(Query.t(), keyword, Preparation.context()) :: Query.t()
+ @spec prepare(Query.t(), keyword, Preparation.Context.t()) :: Query.t()
def prepare(query, _opts, _context) do
case Info.strategy_for_action(query.resource, query.action.name) do
:error ->
@@ -70,7 +70,7 @@ defmodule AshAuthentication.Strategy.OAuth2.SignInPreparation do
|> case do
{:ok, _identity} ->
user
- |> query.api.load(strategy.identity_relationship_name)
+ |> query.domain.load(strategy.identity_relationship_name)
{:error, reason} ->
{:error, reason}
diff --git a/lib/ash_authentication/strategies/password.ex b/lib/ash_authentication/strategies/password.ex
index f545a9a..ea9cb1e 100644
--- a/lib/ash_authentication/strategies/password.ex
+++ b/lib/ash_authentication/strategies/password.ex
@@ -18,7 +18,8 @@ defmodule AshAuthentication.Strategy.Password do
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
- extensions: [AshAuthentication]
+ extensions: [AshAuthentication],
+ domain: MyApp.Accounts
attributes do
uuid_primary_key :id
@@ -27,8 +28,6 @@ defmodule AshAuthentication.Strategy.Password do
end
authentication do
- api MyApp.Accounts
-
strategies do
password :password do
identity_field :email
diff --git a/lib/ash_authentication/strategies/password/actions.ex b/lib/ash_authentication/strategies/password/actions.ex
index adabf5c..e70b847 100644
--- a/lib/ash_authentication/strategies/password/actions.ex
+++ b/lib/ash_authentication/strategies/password/actions.ex
@@ -16,7 +16,7 @@ defmodule AshAuthentication.Strategy.Password.Actions do
{:ok, Resource.record()} | {:error, Errors.AuthenticationFailed.t()}
def sign_in(strategy, params, options)
when is_struct(strategy, Password) and strategy.sign_in_enabled? do
- api = Info.authentication_api!(strategy.resource)
+ domain = Info.domain!(strategy.resource)
{context, options} = Keyword.pop(options, :context, [])
@@ -33,7 +33,7 @@ defmodule AshAuthentication.Strategy.Password.Actions do
|> Query.new()
|> Query.set_context(context)
|> Query.for_read(strategy.sign_in_action_name, params)
- |> api.read(options)
+ |> domain.read(options)
|> case do
{:ok, [user]} ->
{:ok, user}
@@ -101,13 +101,13 @@ defmodule AshAuthentication.Strategy.Password.Actions do
"""
@spec sign_in_with_token(Password.t(), map, keyword) :: {:ok, Resource.record()} | {:error, any}
def sign_in_with_token(strategy, params, options) when is_struct(strategy, Password) do
- api = Info.authentication_api!(strategy.resource)
+ domain = Info.domain!(strategy.resource)
strategy.resource
|> Query.new()
|> Query.set_context(%{private: %{ash_authentication?: true}})
|> Query.for_read(strategy.sign_in_with_token_action_name, params)
- |> api.read(options)
+ |> domain.read(options)
|> case do
{:ok, [user]} ->
{:ok, user}
@@ -147,7 +147,7 @@ defmodule AshAuthentication.Strategy.Password.Actions do
@spec register(Password.t(), map, keyword) :: {:ok, Resource.record()} | {:error, any}
def register(strategy, params, options)
when is_struct(strategy, Password) and strategy.registration_enabled? == true do
- api = Info.authentication_api!(strategy.resource)
+ domain = Info.domain!(strategy.resource)
strategy.resource
|> Changeset.new()
@@ -157,7 +157,7 @@ defmodule AshAuthentication.Strategy.Password.Actions do
}
})
|> Changeset.for_create(strategy.register_action_name, params)
- |> api.create(options)
+ |> domain.create(options)
end
def register(strategy, _params, _options) when is_struct(strategy, Password) do
@@ -182,7 +182,7 @@ defmodule AshAuthentication.Strategy.Password.Actions do
params,
options
) do
- api = Info.authentication_api!(strategy.resource)
+ domain = Info.domain!(strategy.resource)
strategy.resource
|> Query.new()
@@ -192,7 +192,7 @@ defmodule AshAuthentication.Strategy.Password.Actions do
}
})
|> Query.for_read(resettable.request_password_reset_action_name, params)
- |> api.read(options)
+ |> domain.read(options)
|> case do
{:ok, _} -> :ok
{:error, reason} -> {:error, reason}
@@ -216,7 +216,7 @@ defmodule AshAuthentication.Strategy.Password.Actions do
with {:ok, token} <- Map.fetch(params, "reset_token"),
{:ok, %{"sub" => subject}, resource} <- Jwt.verify(token, strategy.resource),
{:ok, user} <- AshAuthentication.subject_to_user(subject, resource, options) do
- api = Info.authentication_api!(resource)
+ domain = Info.domain!(resource)
user
|> Changeset.new()
@@ -226,7 +226,7 @@ defmodule AshAuthentication.Strategy.Password.Actions do
}
})
|> Changeset.for_update(resettable.password_reset_action_name, params)
- |> api.update(options)
+ |> domain.update(options)
else
{:error, %Changeset{} = changeset} -> {:error, changeset}
_ -> {:error, Errors.InvalidToken.exception(type: :reset)}
diff --git a/lib/ash_authentication/strategies/password/password_confirmation_validation.ex b/lib/ash_authentication/strategies/password/password_confirmation_validation.ex
index 052cf10..62f8f14 100644
--- a/lib/ash_authentication/strategies/password/password_confirmation_validation.ex
+++ b/lib/ash_authentication/strategies/password/password_confirmation_validation.ex
@@ -28,7 +28,14 @@ defmodule AshAuthentication.Strategy.Password.PasswordConfirmationValidation do
"""
use Ash.Resource.Validation
- alias Ash.{Changeset, Error.Changes.InvalidArgument, Error.Framework.AssumptionFailed}
+
+ alias Ash.{
+ Changeset,
+ Error.Changes.InvalidArgument,
+ Error.Framework.AssumptionFailed,
+ Resource.Validation
+ }
+
alias AshAuthentication.Info
@doc """
@@ -36,8 +43,9 @@ defmodule AshAuthentication.Strategy.Password.PasswordConfirmationValidation do
equivalent values - if confirmation is required.
"""
@impl true
- @spec validate(Changeset.t(), keyword) :: :ok | {:error, String.t() | Exception.t()}
- def validate(changeset, options) do
+ @spec validate(Changeset.t(), keyword, Validation.Context.t()) ::
+ :ok | {:error, String.t() | Exception.t()}
+ def validate(changeset, options, _context) do
case Info.find_strategy(changeset, options) do
{:ok, %{confirmation_required?: true} = strategy} ->
validate_password_confirmation(changeset, strategy)
diff --git a/lib/ash_authentication/strategies/password/password_validation.ex b/lib/ash_authentication/strategies/password/password_validation.ex
index 4aac19e..d3df61e 100644
--- a/lib/ash_authentication/strategies/password/password_validation.ex
+++ b/lib/ash_authentication/strategies/password/password_validation.ex
@@ -43,14 +43,14 @@ defmodule AshAuthentication.Strategy.Password.PasswordValidation do
"""
use Ash.Resource.Validation
- alias Ash.Changeset
+ alias Ash.{Changeset, Resource.Validation}
alias AshAuthentication.{Errors.AuthenticationFailed, Info}
require Logger
@doc false
@impl true
- @spec validate(Changeset.t(), keyword) :: :ok | {:error, Exception.t()}
- def validate(changeset, options) do
+ @spec validate(Changeset.t(), keyword, Validation.Context.t()) :: :ok | {:error, Exception.t()}
+ def validate(changeset, options, _context) do
{:ok, strategy} = get_strategy(changeset, options)
with {:ok, password_arg} <- get_password_arg(changeset, options, strategy),
diff --git a/lib/ash_authentication/strategies/password/request_password_reset_preparation.ex b/lib/ash_authentication/strategies/password/request_password_reset_preparation.ex
index b3cbc45..14b1068 100644
--- a/lib/ash_authentication/strategies/password/request_password_reset_preparation.ex
+++ b/lib/ash_authentication/strategies/password/request_password_reset_preparation.ex
@@ -18,7 +18,7 @@ defmodule AshAuthentication.Strategy.Password.RequestPasswordResetPreparation do
@doc false
@impl true
- @spec prepare(Query.t(), keyword, Preparation.context()) :: Query.t()
+ @spec prepare(Query.t(), keyword, Preparation.Context.t()) :: Query.t()
def prepare(query, _opts, _context) do
strategy = Info.strategy_for_action!(query.resource, query.action.name)
@@ -28,7 +28,7 @@ defmodule AshAuthentication.Strategy.Password.RequestPasswordResetPreparation do
select_for_senders = Info.authentication_select_for_senders!(query.resource)
query
- |> Query.filter(ref(^identity_field) == ^identity)
+ |> Query.filter(^ref(identity_field) == ^identity)
|> Query.before_action(fn query ->
Ash.Query.ensure_selected(query, select_for_senders)
end)
diff --git a/lib/ash_authentication/strategies/password/reset_token_validation.ex b/lib/ash_authentication/strategies/password/reset_token_validation.ex
index cd2f2ef..7f014bd 100644
--- a/lib/ash_authentication/strategies/password/reset_token_validation.ex
+++ b/lib/ash_authentication/strategies/password/reset_token_validation.ex
@@ -4,13 +4,13 @@ defmodule AshAuthentication.Strategy.Password.ResetTokenValidation do
"""
use Ash.Resource.Validation
- alias Ash.{Changeset, Error.Changes.InvalidArgument}
+ alias Ash.{Changeset, Error.Changes.InvalidArgument, Resource.Validation}
alias AshAuthentication.{Info, Jwt}
@doc false
@impl true
- @spec validate(Changeset.t(), keyword) :: :ok | {:error, Exception.t()}
- def validate(changeset, _) do
+ @spec validate(Changeset.t(), keyword, Validation.Context.t()) :: :ok | {:error, Exception.t()}
+ def validate(changeset, _, _) do
with {:ok, strategy} <- Info.strategy_for_action(changeset.resource, changeset.action.name),
token when is_binary(token) <- Changeset.get_argument(changeset, :reset_token),
{:ok, %{"act" => token_action}, _} <- Jwt.verify(token, changeset.resource),
diff --git a/lib/ash_authentication/strategies/password/sign_in_preparation.ex b/lib/ash_authentication/strategies/password/sign_in_preparation.ex
index 635abaf..51cefb5 100644
--- a/lib/ash_authentication/strategies/password/sign_in_preparation.ex
+++ b/lib/ash_authentication/strategies/password/sign_in_preparation.ex
@@ -19,14 +19,14 @@ defmodule AshAuthentication.Strategy.Password.SignInPreparation do
@doc false
@impl true
- @spec prepare(Query.t(), keyword, Preparation.context()) :: Query.t()
+ @spec prepare(Query.t(), keyword, Preparation.Context.t()) :: Query.t()
def prepare(query, options, context) do
{:ok, strategy} = Info.find_strategy(query, context, options)
identity_field = strategy.identity_field
identity = Query.get_argument(query, identity_field)
query
- |> Query.filter(ref(^identity_field) == ^identity)
+ |> Query.filter(^ref(identity_field) == ^identity)
|> check_sign_in_token_configuration(strategy)
|> Query.before_action(fn query ->
Ash.Query.ensure_selected(query, [strategy.hashed_password_field])
diff --git a/lib/ash_authentication/strategies/password/sign_in_with_token_preparation.ex b/lib/ash_authentication/strategies/password/sign_in_with_token_preparation.ex
index e769e95..2bc1a58 100644
--- a/lib/ash_authentication/strategies/password/sign_in_with_token_preparation.ex
+++ b/lib/ash_authentication/strategies/password/sign_in_with_token_preparation.ex
@@ -12,7 +12,7 @@ defmodule AshAuthentication.Strategy.Password.SignInWithTokenPreparation do
@doc false
@impl true
- @spec prepare(Query.t(), keyword, Preparation.context()) :: Query.t()
+ @spec prepare(Query.t(), keyword, Preparation.Context.t()) :: Query.t()
def prepare(query, options, context) do
{:ok, strategy} = Info.find_strategy(query, context, options)
diff --git a/lib/ash_authentication/strategies/password/transformer.ex b/lib/ash_authentication/strategies/password/transformer.ex
index de01044..f0f6c52 100644
--- a/lib/ash_authentication/strategies/password/transformer.ex
+++ b/lib/ash_authentication/strategies/password/transformer.ex
@@ -96,7 +96,8 @@ defmodule AshAuthentication.Strategy.Password.Transformer do
with {:ok, resource} <- persisted_option(dsl_state, :module),
{:ok, attribute} <- find_attribute(dsl_state, identity_field),
:ok <- validate_attribute_option(attribute, resource, :writable?, [true]),
- :ok <- validate_attribute_option(attribute, resource, :allow_nil?, [false]) do
+ :ok <- validate_attribute_option(attribute, resource, :allow_nil?, [false]),
+ :ok <- validate_attribute_option(attribute, resource, :public?, [true]) do
validate_attribute_unique_constraint(dsl_state, [identity_field], resource)
end
end
@@ -104,8 +105,9 @@ defmodule AshAuthentication.Strategy.Password.Transformer do
defp validate_hashed_password_field(hashed_password_field, dsl_state) do
with {:ok, resource} <- persisted_option(dsl_state, :module),
{:ok, attribute} <- find_attribute(dsl_state, hashed_password_field),
- :ok <- validate_attribute_option(attribute, resource, :writable?, [true]) do
- validate_attribute_option(attribute, resource, :sensitive?, [true])
+ :ok <- validate_attribute_option(attribute, resource, :writable?, [true]),
+ :ok <- validate_attribute_option(attribute, resource, :sensitive?, [true]) do
+ validate_attribute_option(attribute, resource, :public?, [false])
end
end
@@ -498,7 +500,8 @@ defmodule AshAuthentication.Strategy.Password.Transformer do
arguments: arguments,
changes: changes,
metadata: metadata,
- accept: []
+ accept: [],
+ require_atomic?: false
)
end
diff --git a/lib/ash_authentication/strategy.ex b/lib/ash_authentication/strategy.ex
index cbefea6..23e0029 100644
--- a/lib/ash_authentication/strategy.ex
+++ b/lib/ash_authentication/strategy.ex
@@ -120,7 +120,7 @@ defprotocol AshAuthentication.Strategy do
See `actions/1` for a list of actions provided by the strategy.
- Any options passed to the action will be passed to the underlying `Ash.Api` function.
+ Any options passed to the action will be passed to the underlying `Ash.Domain` function.
"""
@spec action(t, action, params :: map, options :: keyword) ::
:ok | {:ok, Resource.record()} | {:error, any}
diff --git a/lib/ash_authentication/supervisor.ex b/lib/ash_authentication/supervisor.ex
index fa3921a..d90fc36 100644
--- a/lib/ash_authentication/supervisor.ex
+++ b/lib/ash_authentication/supervisor.ex
@@ -41,7 +41,7 @@ defmodule AshAuthentication.Supervisor do
raise """
No otp_app provided to AshAuthentication.Supervisor.
- In order to find your Ash APIs and resources you need to provide the
+ In order to find your Ash domains and resources you need to provide the
name of your OTP application when starting AshAuthentication.Supervisor:
Suggestion, try adding `{AshAuthentication.Supervisor, otp_app: :my_app}`
diff --git a/lib/ash_authentication/token_resource.ex b/lib/ash_authentication/token_resource.ex
index 7434a27..c38ddaf 100644
--- a/lib/ash_authentication/token_resource.ex
+++ b/lib/ash_authentication/token_resource.ex
@@ -5,12 +5,13 @@ defmodule AshAuthentication.TokenResource do
%Spark.Dsl.Section{
name: :token,
describe: "Configuration options for this token resource",
- modules: [:api],
+ modules: [:domain],
schema: [
- api: [
- type: {:behaviour, Ash.Api},
+ domain: [
+ type: {:behaviour, Ash.Domain},
+ required: false,
doc: """
- The Ash API to use to access this resource.
+ The Ash domain to use to access this resource.
"""
],
expunge_expired_action_name: [
@@ -117,11 +118,8 @@ defmodule AshAuthentication.TokenResource do
defmodule MyApp.Accounts.Token do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
- extensions: [AshAuthentication.TokenResource]
-
- token do
- api MyApp.Accounts
- end
+ extensions: [AshAuthentication.TokenResource],
+ domain: MyApp.Accounts
postgres do
table "tokens"
diff --git a/lib/ash_authentication/token_resource/actions.ex b/lib/ash_authentication/token_resource/actions.ex
index 3f5496b..d79f173 100644
--- a/lib/ash_authentication/token_resource/actions.ex
+++ b/lib/ash_authentication/token_resource/actions.ex
@@ -12,7 +12,7 @@ defmodule AshAuthentication.TokenResource.Actions do
@spec read_expired(Resource.t(), keyword) :: {:ok, [Resource.record()]} | {:error, any}
def read_expired(resource, opts \\ []) do
with :ok <- assert_resource_has_extension(resource, TokenResource),
- {:ok, api} <- Info.token_api(resource),
+ {:ok, domain} <- Info.token_domain(resource),
{:ok, read_expired_action_name} <- Info.token_read_expired_action_name(resource) do
resource
|> Query.new()
@@ -22,7 +22,7 @@ defmodule AshAuthentication.TokenResource.Actions do
}
})
|> Query.for_read(read_expired_action_name, opts)
- |> api.read()
+ |> domain.read()
end
end
@@ -70,7 +70,7 @@ defmodule AshAuthentication.TokenResource.Actions do
@spec token_revoked?(Resource.t(), String.t(), keyword) :: boolean
def token_revoked?(resource, token, opts \\ []) do
with :ok <- assert_resource_has_extension(resource, TokenResource),
- {:ok, api} <- Info.token_api(resource),
+ {:ok, domain} <- Info.token_domain(resource),
{:ok, is_revoked_action_name} <- Info.token_revocation_is_revoked_action_name(resource) do
resource
|> Query.new()
@@ -80,7 +80,7 @@ defmodule AshAuthentication.TokenResource.Actions do
}
})
|> Query.for_read(is_revoked_action_name, %{"token" => token}, opts)
- |> api.read()
+ |> domain.read()
|> case do
{:ok, []} -> false
{:ok, _} -> true
@@ -98,7 +98,7 @@ defmodule AshAuthentication.TokenResource.Actions do
@spec jti_revoked?(Resource.t(), String.t(), keyword) :: boolean
def jti_revoked?(resource, jti, opts \\ []) do
with :ok <- assert_resource_has_extension(resource, TokenResource),
- {:ok, api} <- Info.token_api(resource),
+ {:ok, domain} <- Info.token_domain(resource),
{:ok, is_revoked_action_name} <- Info.token_revocation_is_revoked_action_name(resource) do
resource
|> Query.new()
@@ -108,7 +108,7 @@ defmodule AshAuthentication.TokenResource.Actions do
}
})
|> Query.for_read(is_revoked_action_name, %{"jti" => jti}, opts)
- |> api.read()
+ |> domain.read()
|> case do
{:ok, []} -> false
{:ok, _} -> true
@@ -130,7 +130,7 @@ defmodule AshAuthentication.TokenResource.Actions do
@spec revoke(Resource.t(), String.t(), keyword) :: :ok | {:error, any}
def revoke(resource, token, opts \\ []) do
with :ok <- assert_resource_has_extension(resource, TokenResource),
- {:ok, api} <- Info.token_api(resource),
+ {:ok, domain} <- Info.token_domain(resource),
{:ok, revoke_token_action_name} <-
Info.token_revocation_revoke_token_action_name(resource) do
resource
@@ -145,7 +145,7 @@ defmodule AshAuthentication.TokenResource.Actions do
%{"token" => token},
Keyword.merge(opts, upsert?: true)
)
- |> api.create()
+ |> domain.create()
|> case do
{:ok, _} -> :ok
{:error, reason} -> {:error, reason}
@@ -161,7 +161,7 @@ defmodule AshAuthentication.TokenResource.Actions do
@spec store_token(Resource.t(), map, keyword) :: :ok | {:error, any}
def store_token(resource, params, opts \\ []) do
with :ok <- assert_resource_has_extension(resource, TokenResource),
- {:ok, api} <- Info.token_api(resource),
+ {:ok, domain} <- Info.token_domain(resource),
{:ok, store_token_action_name} <- Info.token_store_token_action_name(resource) do
resource
|> Changeset.new()
@@ -175,7 +175,7 @@ defmodule AshAuthentication.TokenResource.Actions do
params,
Keyword.merge(opts, upsert?: true)
)
- |> api.create()
+ |> domain.create()
|> case do
{:ok, _} -> :ok
{:error, reason} -> {:error, reason}
@@ -189,45 +189,29 @@ defmodule AshAuthentication.TokenResource.Actions do
@spec get_token(Resource.t(), map, keyword) :: {:ok, [Resource.record()]} | {:error, any}
def get_token(resource, params, opts \\ []) do
with :ok <- assert_resource_has_extension(resource, TokenResource),
- {:ok, api} <- Info.token_api(resource),
+ {:ok, domain} <- Info.token_domain(resource),
{:ok, get_token_action_name} <- Info.token_get_token_action_name(resource) do
resource
|> Query.new()
|> Query.set_context(%{private: %{ash_authentication?: true}})
|> Query.for_read(get_token_action_name, params, opts)
- |> api.read()
+ |> domain.read()
end
end
defp expunge_inside_transaction(resource, expunge_expired_action_name, opts) do
with :ok <- assert_resource_has_extension(resource, TokenResource),
- {:ok, api} <- Info.token_api(resource),
- {:ok, read_expired_action_name} <- Info.token_read_expired_action_name(resource),
- query <-
- resource |> Query.new() |> Query.set_context(%{private: %{ash_authentication?: true}}),
- query <- Query.for_read(query, read_expired_action_name, opts),
- {:ok, expired} <- api.read(query) do
- Enum.reduce_while(expired, {:ok, []}, fn record, {:ok, notifications} ->
- record
- |> Changeset.new()
- |> Changeset.set_context(%{
- private: %{
- ash_authentication?: true
- }
- })
- |> Changeset.for_destroy(expunge_expired_action_name, opts)
- |> api.destroy(return_notifications?: true)
- |> case do
- :ok ->
- {:cont, {:ok, notifications}}
-
- {:ok, more_notifications} ->
- {:cont, {:ok, Enum.concat(notifications, more_notifications)}}
-
- {:error, reason} ->
- {:halt, {:error, reason}}
- end
- end)
+ {:ok, domain} <- Info.token_domain(resource),
+ {:ok, read_expired_action_name} <- Info.token_read_expired_action_name(resource) do
+ resource
+ |> Query.new()
+ |> Query.set_context(%{private: %{ash_authentication?: true}})
+ |> Query.for_read(read_expired_action_name, opts)
+ |> domain.bulk_destroy(expunge_expired_action_name, %{}, opts)
+ |> case do
+ %{status: :success, notifications: notifications} -> {:ok, notifications}
+ %{errors: errors} -> {:error, Ash.Error.to_class(errors)}
+ end
end
end
end
diff --git a/lib/ash_authentication/token_resource/expunger.ex b/lib/ash_authentication/token_resource/expunger.ex
index b05e349..1824a87 100644
--- a/lib/ash_authentication/token_resource/expunger.ex
+++ b/lib/ash_authentication/token_resource/expunger.ex
@@ -8,10 +8,10 @@ defmodule AshAuthentication.TokenResource.Expunger do
```elixir
defmodule MyApp.Accounts.Token do
use Ash.Resource,
- extensions: [AshAuthentication.TokenResource]
+ extensions: [AshAuthentication.TokenResource],
+ domain: MyApp.Accounts
token do
- api MyApp.Accounts
expunge_interval 12
end
end
diff --git a/lib/ash_authentication/token_resource/get_confirmation_changes_preparation.ex b/lib/ash_authentication/token_resource/get_confirmation_changes_preparation.ex
index d801451..23dda97 100644
--- a/lib/ash_authentication/token_resource/get_confirmation_changes_preparation.ex
+++ b/lib/ash_authentication/token_resource/get_confirmation_changes_preparation.ex
@@ -11,7 +11,7 @@ defmodule AshAuthentication.TokenResource.GetConfirmationChangesPreparation do
@doc false
@impl true
- @spec prepare(Query.t(), keyword, Preparation.context()) :: Query.t()
+ @spec prepare(Query.t(), keyword, Preparation.Context.t()) :: Query.t()
def prepare(query, _, _) do
jti = Query.get_argument(query, :jti)
strategy = query.context.strategy
diff --git a/lib/ash_authentication/token_resource/get_token_preparation.ex b/lib/ash_authentication/token_resource/get_token_preparation.ex
index 6703ee4..7cbb6cc 100644
--- a/lib/ash_authentication/token_resource/get_token_preparation.ex
+++ b/lib/ash_authentication/token_resource/get_token_preparation.ex
@@ -11,7 +11,7 @@ defmodule AshAuthentication.TokenResource.GetTokenPreparation do
@doc false
@impl true
- @spec prepare(Query.t(), keyword, Preparation.context()) :: Query.t()
+ @spec prepare(Query.t(), keyword, Preparation.Context.t()) :: Query.t()
def prepare(query, _, _) do
jti = get_jti(query)
purpose = Query.get_argument(query, :purpose)
diff --git a/lib/ash_authentication/token_resource/is_revoked_preparation.ex b/lib/ash_authentication/token_resource/is_revoked_preparation.ex
index 6e5a5d9..bb24fb1 100644
--- a/lib/ash_authentication/token_resource/is_revoked_preparation.ex
+++ b/lib/ash_authentication/token_resource/is_revoked_preparation.ex
@@ -11,7 +11,7 @@ defmodule AshAuthentication.TokenResource.IsRevokedPreparation do
@doc false
@impl true
- @spec prepare(Query.t(), keyword, Preparation.context()) :: Query.t()
+ @spec prepare(Query.t(), keyword, Preparation.Context.t()) :: Query.t()
def prepare(query, _opts, _context) do
case get_jti(query) do
{:ok, jti} ->
diff --git a/lib/ash_authentication/token_resource/transformer.ex b/lib/ash_authentication/token_resource/transformer.ex
index c17be3c..b4bc019 100644
--- a/lib/ash_authentication/token_resource/transformer.ex
+++ b/lib/ash_authentication/token_resource/transformer.ex
@@ -34,13 +34,14 @@ defmodule AshAuthentication.TokenResource.Transformer do
@spec transform(map) ::
:ok | {:ok, map} | {:error, term} | {:warn, map, String.t() | [String.t()]} | :halt
def transform(dsl_state) do
- with {:ok, dsl_state} <- maybe_set_api(dsl_state, :token),
+ with {:ok, dsl_state} <- maybe_set_domain(dsl_state, :token),
{:ok, dsl_state} <-
maybe_build_attribute(dsl_state, :jti, :string,
primary_key?: true,
allow_nil?: false,
sensitive?: true,
- writable?: true
+ writable?: true,
+ public?: true
),
:ok <- validate_jti_field(dsl_state),
{:ok, dsl_state} <-
@@ -55,24 +56,26 @@ defmodule AshAuthentication.TokenResource.Transformer do
{:ok, dsl_state} <-
maybe_build_attribute(dsl_state, :purpose, :string,
allow_nil?: false,
- writable?: true
+ writable?: true,
+ public?: true
),
:ok <- validate_purpose_field(dsl_state),
{:ok, dsl_state} <-
maybe_build_attribute(dsl_state, :extra_data, :map,
allow_nil?: true,
- writable?: true
+ writable?: true,
+ public?: true
),
{:ok, dsl_state} <-
maybe_build_attribute(dsl_state, :created_at, :utc_datetime_usec,
allow_nil?: false,
- private?: true,
+ public?: false,
default: &DateTime.utc_now/0
),
{:ok, dsl_state} <-
maybe_build_attribute(dsl_state, :updated_at, :utc_datetime_usec,
allow_nil?: false,
- private?: true,
+ public?: false,
default: &DateTime.utc_now/0,
update_default: &DateTime.utc_now/0
),
@@ -311,11 +314,16 @@ defmodule AshAuthentication.TokenResource.Transformer do
end
defp build_read_expired_action(_dsl_state, action_name) do
- import Ash.Filter.TemplateHelpers
+ import Ash.Expr
+
+ filter =
+ Transformer.build_entity!(Resource.Dsl, [:actions, :read], :filter,
+ filter: expr(expires_at < now())
+ )
Transformer.build_entity(Resource.Dsl, [:actions], :read,
name: action_name,
- filter: expr(expires_at < now())
+ filters: [filter]
)
end
@@ -421,7 +429,7 @@ defmodule AshAuthentication.TokenResource.Transformer do
:ok <- validate_attribute_option(attribute, resource, :sensitive?, [true]),
:ok <- validate_attribute_option(attribute, resource, :writable?, [true]),
:ok <- validate_attribute_option(attribute, resource, :primary_key?, [true]) do
- validate_attribute_option(attribute, resource, :private?, [false])
+ validate_attribute_option(attribute, resource, :public?, [true])
end
end
@@ -439,8 +447,9 @@ defmodule AshAuthentication.TokenResource.Transformer do
with {:ok, resource} <- persisted_option(dsl_state, :module),
{:ok, attribute} <- find_attribute(dsl_state, :purpose),
:ok <- validate_attribute_option(attribute, resource, :type, [Type.String, :string]),
- :ok <- validate_attribute_option(attribute, resource, :allow_nil?, [false]) do
- validate_attribute_option(attribute, resource, :writable?, [true])
+ :ok <- validate_attribute_option(attribute, resource, :allow_nil?, [false]),
+ :ok <- validate_attribute_option(attribute, resource, :writable?, [true]) do
+ validate_attribute_option(attribute, resource, :public?, [true])
end
end
@@ -448,8 +457,9 @@ defmodule AshAuthentication.TokenResource.Transformer do
with {:ok, resource} <- persisted_option(dsl_state, :module),
{:ok, attribute} <- find_attribute(dsl_state, :extra_data),
:ok <- validate_attribute_option(attribute, resource, :type, [Type.Map, :map]),
- :ok <- validate_attribute_option(attribute, resource, :allow_nil?, [true]) do
- validate_attribute_option(attribute, resource, :writable?, [true])
+ :ok <- validate_attribute_option(attribute, resource, :allow_nil?, [true]),
+ :ok <- validate_attribute_option(attribute, resource, :writable?, [true]) do
+ validate_attribute_option(attribute, resource, :public?, [true])
end
end
end
diff --git a/lib/ash_authentication/token_resource/verifier.ex b/lib/ash_authentication/token_resource/verifier.ex
index 8ee964b..4920789 100644
--- a/lib/ash_authentication/token_resource/verifier.ex
+++ b/lib/ash_authentication/token_resource/verifier.ex
@@ -28,28 +28,28 @@ defmodule AshAuthentication.TokenResource.Verifier do
@spec transform(map) ::
:ok | {:ok, map} | {:error, term} | {:warn, map, String.t() | [String.t()]} | :halt
def transform(dsl_state) do
- validate_api_presence(dsl_state)
+ validate_domain_presence(dsl_state)
end
- defp validate_api_presence(dsl_state) do
- with api when not is_nil(api) <- Transformer.get_option(dsl_state, [:token], :api),
- :ok <- assert_is_module(api),
- true <- function_exported?(api, :spark_is, 0),
- Ash.Api <- api.spark_is() do
- {:ok, api}
+ defp validate_domain_presence(dsl_state) do
+ with domain when not is_nil(domain) <- Transformer.get_option(dsl_state, [:token], :domain),
+ :ok <- assert_is_module(domain),
+ true <- function_exported?(domain, :spark_is, 0),
+ Ash.Domain <- domain.spark_is() do
+ {:ok, domain}
else
nil ->
{:error,
DslError.exception(
- path: [:token, :api],
- message: "An API module must be present"
+ path: [:token, :domain],
+ message: "A domain module must be present"
)}
_ ->
{:error,
DslError.exception(
- path: [:token, :api],
- message: "Module is not an Ash.Api."
+ path: [:token, :domain],
+ message: "Module is not an `Ash.Domain`."
)}
end
end
diff --git a/lib/ash_authentication/transformer.ex b/lib/ash_authentication/transformer.ex
index a253424..79c5bdf 100644
--- a/lib/ash_authentication/transformer.ex
+++ b/lib/ash_authentication/transformer.ex
@@ -30,7 +30,7 @@ defmodule AshAuthentication.Transformer do
@spec transform(map) ::
:ok | {:ok, map} | {:error, term} | {:warn, map, String.t() | [String.t()]} | :halt
def transform(dsl_state) do
- with {:ok, dsl_state} <- maybe_set_api(dsl_state, :authentication),
+ with {:ok, dsl_state} <- maybe_set_domain(dsl_state, :authentication),
:ok <- validate_at_least_one_strategy(dsl_state),
:ok <- validate_unique_strategy_names(dsl_state),
:ok <- validate_unique_add_on_names(dsl_state),
diff --git a/lib/ash_authentication/user_identity.ex b/lib/ash_authentication/user_identity.ex
index 785cc47..99bf222 100644
--- a/lib/ash_authentication/user_identity.ex
+++ b/lib/ash_authentication/user_identity.ex
@@ -3,11 +3,12 @@ defmodule AshAuthentication.UserIdentity do
%Spark.Dsl.Section{
name: :user_identity,
describe: "Configure identity options for this resource",
- modules: [:api, :user_resource],
+ modules: [:domain, :user_resource],
schema: [
- api: [
- type: {:behaviour, Ash.Api},
- doc: "The Ash API to use to access this resource."
+ domain: [
+ type: {:behaviour, Ash.Domain},
+ doc: "The Ash domain to use to access this resource.",
+ required: false
],
user_resource: [
type: {:behaviour, Ash.Resource},
@@ -95,10 +96,10 @@ defmodule AshAuthentication.UserIdentity do
defmodule MyApp.Accounts.UserIdentity do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
- extensions: [AshAuthentication.UserIdentity]
+ extensions: [AshAuthentication.UserIdentity],
+ domain: MyApp.Accounts
user_identity do
- api MyApp.Accounts
user_resource MyApp.Accounts.User
end
diff --git a/lib/ash_authentication/user_identity/actions.ex b/lib/ash_authentication/user_identity/actions.ex
index d91af12..501f807 100644
--- a/lib/ash_authentication/user_identity/actions.ex
+++ b/lib/ash_authentication/user_identity/actions.ex
@@ -3,7 +3,7 @@ defmodule AshAuthentication.UserIdentity.Actions do
Code interface for provider identity actions.
Allows you to interact with UserIdentity resources without having to mess
- around with changesets, apis, etc. These functions are delegated to from
+ around with changesets, domains, etc. These functions are delegated to from
within `AshAuthentication.UserIdentity`.
"""
@@ -15,7 +15,7 @@ defmodule AshAuthentication.UserIdentity.Actions do
"""
@spec upsert(Resource.t(), map) :: {:ok, Resource.record()} | {:error, term}
def upsert(resource, attributes) do
- with {:ok, api} <- UserIdentity.Info.user_identity_api(resource),
+ with {:ok, domain} <- UserIdentity.Info.user_identity_domain(resource),
{:ok, upsert_action_name} <-
UserIdentity.Info.user_identity_upsert_action_name(resource),
action when is_map(action) <- Resource.Info.action(resource, upsert_action_name) do
@@ -30,7 +30,7 @@ defmodule AshAuthentication.UserIdentity.Actions do
upsert?: true,
upsert_identity: action.upsert_identity
)
- |> api.create()
+ |> domain.create()
end
end
end
diff --git a/lib/ash_authentication/user_identity/transformer.ex b/lib/ash_authentication/user_identity/transformer.ex
index c8c0c23..171371a 100644
--- a/lib/ash_authentication/user_identity/transformer.ex
+++ b/lib/ash_authentication/user_identity/transformer.ex
@@ -34,7 +34,7 @@ defmodule AshAuthentication.UserIdentity.Transformer do
@spec transform(map) ::
:ok | {:ok, map} | {:error, term} | {:warn, map, String.t() | [String.t()]} | :halt
def transform(dsl_state) do
- with {:ok, dsl_state} <- maybe_set_api(dsl_state, :user_identity),
+ with {:ok, dsl_state} <- maybe_set_domain(dsl_state, :user_identity),
{:ok, resource} <- persisted_option(dsl_state, :module),
{:ok, dsl_state} <-
maybe_build_attribute(dsl_state, :id, Type.UUID,
@@ -58,7 +58,8 @@ defmodule AshAuthentication.UserIdentity.Transformer do
{:ok, dsl_state} <-
maybe_build_attribute(dsl_state, strategy, Type.String,
allow_nil?: false,
- writable?: true
+ writable?: true,
+ public?: true
),
:ok <- validate_strategy_field(dsl_state, strategy),
{:ok, dsl_state} <-
@@ -134,7 +135,8 @@ defmodule AshAuthentication.UserIdentity.Transformer do
{:ok, attribute} <- find_attribute(dsl_state, field_name),
:ok <- validate_attribute_option(attribute, resource, :type, [Type.String, :string]),
:ok <- validate_attribute_option(attribute, resource, :allow_nil?, [false]),
- :ok <- validate_attribute_option(attribute, resource, :writable?, [true]) do
+ :ok <- validate_attribute_option(attribute, resource, :writable?, [true]),
+ :ok <- validate_attribute_option(attribute, resource, :public?, [true]) do
:ok
else
{:error, reason} -> {:error, reason}
@@ -183,7 +185,7 @@ defmodule AshAuthentication.UserIdentity.Transformer do
defp build_user_relationship(dsl_state, name, destination) do
with {:ok, id_attr} <- find_pk(destination),
- {:ok, api} <- AshAuthentication.Info.authentication_api(destination),
+ {:ok, domain} <- AshAuthentication.Info.domain(destination),
{:ok, user_id} <-
UserIdentity.Info.user_identity_user_id_attribute_name(dsl_state) do
Transformer.build_entity(Resource.Dsl, [:relationships], :belongs_to,
@@ -193,7 +195,7 @@ defmodule AshAuthentication.UserIdentity.Transformer do
destination_attribute: id_attr.name,
attribute_type: id_attr.type,
source_attribute: user_id,
- api: api,
+ domain: domain,
attribute_writable?: true,
writable?: true
)
@@ -202,14 +204,14 @@ defmodule AshAuthentication.UserIdentity.Transformer do
defp validate_user_relationship(dsl_state, name, destination) do
with {:ok, id_attr} <- find_pk(destination),
- {:ok, api} <- AshAuthentication.Info.authentication_api(destination),
+ {:ok, domain} <- AshAuthentication.Info.domain(destination),
{:ok, relationship} <- find_relationship(dsl_state, name),
{:ok, user_id} <-
UserIdentity.Info.user_identity_user_id_attribute_name(dsl_state),
:ok <- validate_field_in_values(relationship, :destination, [destination]),
:ok <- validate_field_in_values(relationship, :destination_attribute, [id_attr.name]),
:ok <- validate_field_in_values(relationship, :source_attribute, [user_id]),
- :ok <- validate_field_in_values(relationship, :api, [api]) do
+ :ok <- validate_field_in_values(relationship, :domain, [domain]) do
validate_field_in_values(relationship, :attribute_type, [id_attr.type])
end
end
diff --git a/lib/ash_authentication/user_identity/verifier.ex b/lib/ash_authentication/user_identity/verifier.ex
index 808e34c..1633c47 100644
--- a/lib/ash_authentication/user_identity/verifier.ex
+++ b/lib/ash_authentication/user_identity/verifier.ex
@@ -27,14 +27,14 @@ defmodule AshAuthentication.UserIdentity.Verifier do
@spec transform(map) ::
:ok | {:ok, map} | {:error, term} | {:warn, map, String.t() | [String.t()]} | :halt
def transform(dsl_state) do
- with :ok <- validate_api_presence(dsl_state) do
+ with :ok <- validate_domain_presence(dsl_state) do
validate_user_resource(dsl_state)
end
end
- defp validate_api_presence(dsl_state) do
- with {:ok, api} <- Info.user_identity_api(dsl_state) do
- assert_is_api(api)
+ defp validate_domain_presence(dsl_state) do
+ with {:ok, domain} <- Info.user_identity_domain(dsl_state) do
+ assert_is_domain(domain)
end
end
diff --git a/lib/ash_authentication/utils.ex b/lib/ash_authentication/utils.ex
index 8f9515e..d86abf7 100644
--- a/lib/ash_authentication/utils.ex
+++ b/lib/ash_authentication/utils.ex
@@ -1,6 +1,6 @@
defmodule AshAuthentication.Utils do
@moduledoc false
- alias Ash.{Api, Resource}
+ alias Ash.{Domain, Resource}
alias Spark.{Dsl, Dsl.Transformer}
@doc """
@@ -80,13 +80,13 @@ defmodule AshAuthentication.Utils do
def maybe_concat(collection, _test, new_elements), do: Enum.concat(collection, new_elements)
@doc """
- Used within transformers to infer `api` from a resource if the option is not set.
+ Used within transformers to infer `domain` from a resource if the option is not set.
"""
- def maybe_set_api(dsl_state, section) do
- api = Transformer.get_persisted(dsl_state, :api)
+ def maybe_set_domain(dsl_state, section) do
+ domain = Transformer.get_persisted(dsl_state, :domain)
- if api && !Transformer.get_option(dsl_state, [section], :api) do
- {:ok, Transformer.set_option(dsl_state, [section], :api, api)}
+ if domain && !Transformer.get_option(dsl_state, [section], :domain) do
+ {:ok, Transformer.set_option(dsl_state, [section], :domain, domain)}
else
{:ok, dsl_state}
end
@@ -209,16 +209,16 @@ defmodule AshAuthentication.Utils do
end
@doc """
- Asserts that `module` is actually an Ash API.
+ Asserts that `module` is actually an Ash domain.
"""
- @spec assert_is_api(Api.t()) :: :ok | {:error, term}
- def assert_is_api(module) do
+ @spec assert_is_domain(Domain.t()) :: :ok | {:error, term}
+ def assert_is_domain(module) do
with :ok <- assert_is_module(module),
true <- function_exported?(module, :spark_is, 0),
- Api <- module.spark_is() do
+ Domain <- module.spark_is() do
:ok
else
- _ -> {:error, "Module `#{inspect(module)}` is not an Ash API"}
+ _ -> {:error, "Module `#{inspect(module)}` is not an Ash domain"}
end
end
diff --git a/lib/ash_authentication/validations/action.ex b/lib/ash_authentication/validations/action.ex
index a1e7ef7..0e93615 100644
--- a/lib/ash_authentication/validations/action.ex
+++ b/lib/ash_authentication/validations/action.ex
@@ -198,4 +198,52 @@ defmodule AshAuthentication.Validations.Action do
"The action `#{inspect(action.name)}` should have the `#{inspect(preparation_module)}` preparation present."
)}
end
+
+ @doc """
+ Validate the action has the provided option.
+ """
+ @spec validate_action_option(Actions.action(), atom, [any]) :: :ok | {:error, Exception.t()}
+ def validate_action_option(action, field, values) do
+ with {:ok, value} <- Map.fetch(action, field),
+ true <- value in values do
+ :ok
+ else
+ :error ->
+ {:error,
+ DslError.exception(
+ path: [:actions, action.name, field],
+ message:
+ "The action `#{inspect(action.name)}` is missing the `#{inspect(field)}` option set"
+ )}
+
+ false ->
+ case values do
+ [] ->
+ {:error,
+ DslError.exception(
+ path: [:actions, action.name, field],
+ message:
+ "The action `#{inspect(action.name)}` should not have the `#{inspect(field)}` option set"
+ )}
+
+ [expected] ->
+ {:error,
+ DslError.exception(
+ path: [:actions, action.name, field],
+ message:
+ "The action `#{inspect(action.name)}` should have the `#{inspect(field)}` option set to `#{inspect(expected)}`"
+ )}
+
+ expected ->
+ expected = expected |> Enum.map(&"`#{inspect(&1)}`") |> to_sentence(final: "or")
+
+ {:error,
+ DslError.exception(
+ path: [:actions, action.name, field],
+ message:
+ "The action `#{inspect(action.name)}` should have the `#{inspect(field)}` option set to one of #{expected}"
+ )}
+ end
+ end
+ end
end
diff --git a/lib/ash_authentication/validations/attribute.ex b/lib/ash_authentication/validations/attribute.ex
index 20f2c2a..60b5972 100644
--- a/lib/ash_authentication/validations/attribute.ex
+++ b/lib/ash_authentication/validations/attribute.ex
@@ -19,6 +19,7 @@ defmodule AshAuthentication.Validations.Attribute do
:error ->
{:error,
DslError.exception(
+ module: resource,
path: [:actions, :attribute],
message:
"The attribute `#{inspect(attribute.name)}` on the `#{inspect(resource)}` resource is missing the `#{inspect(field)}` property"
@@ -29,6 +30,7 @@ defmodule AshAuthentication.Validations.Attribute do
[] ->
{:error,
DslError.exception(
+ module: resource,
path: [:actions, :attribute],
message:
"The attribute `#{inspect(attribute.name)}` on the `#{inspect(resource)}` resource is should not have `#{inspect(field)}` set"
@@ -37,6 +39,7 @@ defmodule AshAuthentication.Validations.Attribute do
[expected] ->
{:error,
DslError.exception(
+ module: resource,
path: [:actions, :attribute],
message:
"The attribute `#{inspect(attribute.name)}` on the `#{inspect(resource)}` resource should have `#{inspect(field)}` set to `#{inspect(expected)}`"
@@ -47,6 +50,7 @@ defmodule AshAuthentication.Validations.Attribute do
{:error,
DslError.exception(
+ module: resource,
path: [:actions, :attribute],
message:
"The attribute `#{inspect(attribute.name)}` on the `#{inspect(resource)}` resource should have `#{inspect(field)}` set to one of #{expected}"
diff --git a/lib/ash_authentication/verifier.ex b/lib/ash_authentication/verifier.ex
index 1832472..a7bb469 100644
--- a/lib/ash_authentication/verifier.ex
+++ b/lib/ash_authentication/verifier.ex
@@ -17,30 +17,31 @@ defmodule AshAuthentication.Verifier do
| {:error, term}
| {:warn, String.t() | list(String.t())}
def verify(dsl_state) do
- with {:ok, _api} <- validate_api_presence(dsl_state) do
+ with {:ok, _domain} <- validate_domain_presence(dsl_state) do
validate_token_resource(dsl_state)
end
end
- defp validate_api_presence(dsl_state) do
- with api when not is_nil(api) <- Transformer.get_option(dsl_state, [:authentication], :api),
- :ok <- assert_is_module(api),
- true <- function_exported?(api, :spark_is, 0),
- Ash.Api <- api.spark_is() do
- {:ok, api}
+ defp validate_domain_presence(dsl_state) do
+ with domain when not is_nil(domain) <-
+ Transformer.get_option(dsl_state, [:authentication], :domain),
+ :ok <- assert_is_module(domain),
+ true <- function_exported?(domain, :spark_is, 0),
+ Ash.Domain <- domain.spark_is() do
+ {:ok, domain}
else
nil ->
{:error,
DslError.exception(
- path: [:authentication, :api],
- message: "An API module must be present"
+ path: [:authentication, :domain],
+ message: "A domain module must be present"
)}
_ ->
{:error,
DslError.exception(
- path: [:authentication, :api],
- message: "Module is not an Ash.Api."
+ path: [:authentication, :domain],
+ message: "Module is not an `Ash.Domain`."
)}
end
end
diff --git a/mix.exs b/mix.exs
index 4b21848..78e4593 100644
--- a/mix.exs
+++ b/mix.exs
@@ -173,7 +173,7 @@ defmodule AshAuthentication.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
- {:ash, ash_version("~> 2.5 and >= 2.5.11")},
+ {:ash, ash_version("== 3.0.0-rc.1")},
{:assent, "~> 0.2 and >= 0.2.8"},
{:bcrypt_elixir, "~> 3.0"},
{:castore, "~> 1.0"},
@@ -181,16 +181,17 @@ defmodule AshAuthentication.MixProject do
{:jason, "~> 1.4"},
{:joken, "~> 2.5"},
{:plug, "~> 1.13"},
- {:spark, "~> 1.1 and >= 1.1.39"},
+ {:spark, "~> 2.0"},
+ {:splode, "~> 0.2"},
{:absinthe_plug, "~> 1.5", only: [:dev, :test]},
- {:ash_graphql, "~> 0.21", only: [:dev, :test]},
- {:ash_json_api, "~> 0.30", only: [:dev, :test]},
- {:ash_postgres, "~> 1.5.1", optional: true},
+ # {:ash_graphql, "~> 0.21", only: [:dev, :test]},
+ # {:ash_json_api, "~> 0.30", only: [:dev, :test]},
+ {:ash_postgres, "== 2.0.0-rc.1", optional: true},
{:credo, "~> 1.6", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.2", only: [:dev, :test], runtime: false},
{:doctor, "~> 0.18", only: [:dev, :test]},
{:ex_check, "~> 0.15", only: [:dev, :test]},
- {:ex_doc, github: "elixir-lang/ex_doc", only: [:dev, :test], runtime: false},
+ {:ex_doc, ">= 0.0.0", only: [:dev, :test]},
{:faker, "~> 0.18.0", only: [:dev, :test]},
{:git_ops, "~> 2.4", only: [:dev, :test], runtime: false},
{:mimic, "~> 1.7", only: [:dev, :test]},
diff --git a/mix.lock b/mix.lock
index 7519748..2f7505b 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,17 +1,14 @@
%{
- "absinthe": {:hex, :absinthe, "1.7.6", "0b897365f98d068cfcb4533c0200a8e58825a4aeeae6ec33633ebed6de11773b", [:mix], [{:dataloader, "~> 1.0.0 or ~> 2.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:opentelemetry_process_propagator, "~> 0.2.1", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 1.0 or ~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7626951ca5eec627da960615b51009f3a774765406ff02722b1d818f17e5778"},
+ "absinthe": {:hex, :absinthe, "1.7.6", "0b897365f98d068cfcb4533c0200a8e58825a4aeeae6ec33633ebed6de11773b", [:mix], [{:dataloader, "~> 1.0.0 or ~> 2.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:opentelemetry_process_propagator, "~> 0.2.1", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7626951ca5eec627da960615b51009f3a774765406ff02722b1d818f17e5778"},
"absinthe_plug": {:hex, :absinthe_plug, "1.5.8", "38d230641ba9dca8f72f1fed2dfc8abd53b3907d1996363da32434ab6ee5d6ab", [:mix], [{:absinthe, "~> 1.5", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bbb04176647b735828861e7b2705465e53e2cf54ccf5a73ddd1ebd855f996e5a"},
- "ash": {:hex, :ash, "2.21.2", "d62657fc18ee8a519042b03721ab34427ed640d0dbd1ddd79df175dd2876b8f6", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.6", [hex: :reactor, repo: "hexpm", optional: false]}, {:spark, ">= 1.1.55 and < 2.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.6", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b512c51812ef971c1cb2993b55ffc9aa833e251cdbbdbd813657e9533e78c3d9"},
- "ash_graphql": {:hex, :ash_graphql, "0.27.1", "514ea4d3f2dafab45d6d4a0c7eb8037719a693a905295575f1faa069b6791b09", [:mix], [{:absinthe, "~> 1.7", [hex: :absinthe, repo: "hexpm", optional: false]}, {:absinthe_plug, "~> 1.4", [hex: :absinthe_plug, repo: "hexpm", optional: false]}, {:ash, "~> 2.17", [hex: :ash, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "81585f7db55730938f263f78d32e35cea81b6d1139b14ce59cdf59e1ab3a688b"},
- "ash_json_api": {:hex, :ash_json_api, "0.34.2", "21a1f935d1208d7f419f08cb44ae379ffa9919dc4860e6bbc6e7499762986e7e", [:mix], [{:ash, ">= 2.9.24 and < 3.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:json_xema, "~> 0.4.0", [hex: :json_xema, repo: "hexpm", optional: false]}, {:open_api_spex, "~> 3.16", [hex: :open_api_spex, repo: "hexpm", optional: true]}, {:plug, "~> 1.11", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "620658e495ac745807d8eab0e752836f44e1368c98c7beaad5d4c2bd8c286cf4"},
- "ash_postgres": {:hex, :ash_postgres, "1.5.22", "3cad63ffce8080615240f01caf389876dbbadda61a298585d6dc50fa4d48680f", [:mix], [{:ash, ">= 2.20.3 and < 3.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "1056587a9e6aab2b0ed282a9ab70a7e5f3a059eed7c7546e0265225ad62f41fc"},
+ "ash": {:hex, :ash, "3.0.0-rc.1", "ab922a15d85f4c2ee2250908239cf8dda3ffdbdcac4664b7814afdd024428046", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.8", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.1.7 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.6", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf3e579867f3ed15e219789fe47965473b1639aae68c383579fbc4a982e11f85"},
+ "ash_postgres": {:hex, :ash_postgres, "2.0.0-rc.1", "c6f2284ab5c7271df63cf13af82655c8632d32665809d2120861c38bd9ee32b3", [:mix], [{:ash, "~> 3.0.0-rc.0", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:simple_sat, "~> 0.1", [hex: :simple_sat, repo: "hexpm", optional: false]}], "hexpm", "840d95c9ac9e363620428568c22a0312aaa181c74f55289a8bc6588801f22e93"},
"assent": {:hex, :assent, "0.2.9", "e3cdbc8f2e4f8d02c4c490ef8c2148bb1bc0d81aa0648f09addc5918d9a1cd5a", [:mix], [{:certifi, ">= 0.0.0", [hex: :certifi, repo: "hexpm", optional: true]}, {:finch, "~> 0.15", [hex: :finch, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: true]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:req, "~> 0.4", [hex: :req, repo: "hexpm", optional: true]}, {:ssl_verify_fun, ">= 0.0.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: true]}], "hexpm", "5f9562bda90bef7bd3f1b9a348520a5631b86c85145346bb7edb8a7ebbad8e86"},
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.1.0", "0b110a9a6c619b19a7f73fa3004aa11d6e719a67e672d1633dc36b6b2290a0f7", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2ad2acb5a8bc049e8d5aa267802631912bb80d5f4110a178ae7999e69dca1bf7"},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"castore": {:hex, :castore, "1.0.6", "ffc42f110ebfdafab0ea159cd43d31365fa0af0ce4a02ecebf1707ae619ee727", [:mix], [], "hexpm", "374c6e7ca752296be3d6780a6d5b922854ffcc74123da90f2f328996b962d33a"},
"comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"},
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
- "conv_case": {:hex, :conv_case, "0.2.3", "c1455c27d3c1ffcdd5f17f1e91f40b8a0bc0a337805a6e8302f441af17118ed8", [:mix], [], "hexpm", "88f29a3d97d1742f9865f7e394ed3da011abb7c5e8cc104e676fdef6270d4b4a"},
"cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"},
@@ -20,7 +17,6 @@
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
"doctor": {:hex, :doctor, "0.21.0", "20ef89355c67778e206225fe74913e96141c4d001cb04efdeba1a2a9704f1ab5", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "a227831daa79784eb24cdeedfa403c46a4cb7d0eab0e31232ec654314447e4e0"},
- "earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"},
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"},
"ecto_sql": {:hex, :ecto_sql, "3.11.1", "e9abf28ae27ef3916b43545f9578b4750956ccea444853606472089e7d169470", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ce14063ab3514424276e7e360108ad6c2308f6d88164a076aac8a387e1fea634"},
@@ -28,7 +24,7 @@
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"},
"ex_check": {:hex, :ex_check, "0.16.0", "07615bef493c5b8d12d5119de3914274277299c6483989e52b0f6b8358a26b5f", [:mix], [], "hexpm", "4d809b72a18d405514dda4809257d8e665ae7cf37a7aee3be6b74a34dec310f5"},
- "ex_doc": {:git, "https://github.com/elixir-lang/ex_doc.git", "a663c13478a49d29ae0267b6e45badb803267cf0", []},
+ "ex_doc": {:hex, :ex_doc, "0.31.2", "8b06d0a5ac69e1a54df35519c951f1f44a7b7ca9a5bb7a260cd8a174d6322ece", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "317346c14febaba9ca40fd97b5b5919f7751fb85d399cc8e7e8872049f37e0af"},
"faker": {:hex, :faker, "0.18.0", "943e479319a22ea4e8e39e8e076b81c02827d9302f3d32726c5bf82f430e6e14", [:mix], [], "hexpm", "bfbdd83958d78e2788e99ec9317c4816e651ad05e24cfd1196ce5db5b3e81797"},
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
"finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"},
@@ -38,32 +34,31 @@
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"joken": {:hex, :joken, "2.6.0", "b9dd9b6d52e3e6fcb6c65e151ad38bf4bc286382b5b6f97079c47ade6b1bcc6a", [:mix], [{:jose, "~> 1.11.5", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5a95b05a71cd0b54abd35378aeb1d487a23a52c324fa7efdffc512b655b5aaa7"},
"jose": {:hex, :jose, "1.11.6", "613fda82552128aa6fb804682e3a616f4bc15565a048dabd05b1ebd5827ed965", [:mix, :rebar3], [], "hexpm", "6275cb75504f9c1e60eeacb771adfeee4905a9e182103aa59b53fed651ff9738"},
- "json_xema": {:hex, :json_xema, "0.4.2", "85de190f597a98ce9da436b8a59c97ef561a6ab6017255df8b494babefd6fb10", [:mix], [{:conv_case, "~> 0.2", [hex: :conv_case, repo: "hexpm", optional: false]}, {:xema, "~> 0.11", [hex: :xema, repo: "hexpm", optional: false]}], "hexpm", "5516213758667d21669e0d63ea287238d277519527bac6c02140a5e34c1fda80"},
"libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"},
- "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
- "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
- "makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"},
+ "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"},
+ "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
+ "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"},
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
"mimic": {:hex, :mimic, "1.7.4", "cd2772ffbc9edefe964bc668bfd4059487fa639a5b7f1cbdf4fd22946505aa4f", [:mix], [], "hexpm", "437c61041ecf8a7fae35763ce89859e4973bb0666e6ce76d75efc789204447c3"},
"mint": {:hex, :mint, "1.5.2", "4805e059f96028948870d23d7783613b7e6b0e2fb4e98d720383852a760067fd", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "d77d9e9ce4eb35941907f1d3df38d8f750c357865353e21d335bdcdf6d892a02"},
"mix_audit": {:hex, :mix_audit, "2.1.3", "c70983d5cab5dca923f9a6efe559abfb4ec3f8e87762f02bab00fa4106d17eda", [:make, :mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.9", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "8c3987100b23099aea2f2df0af4d296701efd031affb08d0746b2be9e35988ec"},
"nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
- "nimble_pool": {:hex, :nimble_pool, "1.0.0", "5eb82705d138f4dd4423f69ceb19ac667b3b492ae570c9f5c900bb3d2f50a847", [:mix], [], "hexpm", "80be3b882d2d351882256087078e1b1952a28bf98d0a287be87e4a24a710b67a"},
- "picosat_elixir": {:hex, :picosat_elixir, "0.2.3", "bf326d0f179fbb3b706bb2c15fbc367dacfa2517157d090fdfc32edae004c597", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f76c9db2dec9d2561ffaa9be35f65403d53e984e8cd99c832383b7ab78c16c66"},
+ "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"plug": {:hex, :plug, "1.15.3", "712976f504418f6dff0a3e554c40d705a9bcf89a7ccef92fc6a5ef8f16a30a97", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4365a3c010a56af402e0809208873d113e9c38c401cabd88027ef4f5c01fd2"},
"plug_cowboy": {:hex, :plug_cowboy, "2.7.0", "3ae9369c60641084363b08fe90267cbdd316df57e3557ea522114b30b63256ea", [:mix], [{:cowboy, "~> 2.7.0 or ~> 2.8.0 or ~> 2.9.0 or ~> 2.10.0", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "d85444fb8aa1f2fc62eabe83bbe387d81510d773886774ebdcb429b3da3c1a4a"},
"plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"},
"postgrex": {:hex, :postgrex, "0.17.5", "0483d054938a8dc069b21bdd636bf56c487404c241ce6c319c1f43588246b281", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "50b8b11afbb2c4095a3ba675b4f055c416d0f3d7de6633a595fc131a828a67eb"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
- "reactor": {:hex, :reactor, "0.7.0", "fb76d23d95829b28ac9b9d654620c43c890c6a32ea26ac13086c48540b34e8c5", [:mix], [{:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 1.0", [hex: :spark, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4310da820d753aafd7dc4ee8cc687b84565dd6d9536e38806ee211da792178fd"},
+ "reactor": {:hex, :reactor, "0.8.1", "1aec71d16083901277727c8162f6dd0f07e80f5ca98911b6ef4f2c95e6e62758", [:mix], [{:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ae3936d97a3e4a316744f70c77b85345b08b70da334024c26e6b5eb8ede1246b"},
+ "simple_sat": {:hex, :simple_sat, "0.1.1", "68a5ebe6f6d5956bd806e4881c495692c14580a2f1a4420488985abd0fba2119", [:mix], [], "hexpm", "63571218f92ff029838df7645eb8f0c38df8ed60d2d14578412a8d142a94471e"},
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
"sourceror": {:hex, :sourceror, "1.0.2", "c5e86fdc14881f797749d1fe5df017ca66727a8146e7ee3e736605a3df78f3e6", [:mix], [], "hexpm", "832335e87d0913658f129d58b2a7dc0490ddd4487b02de6d85bca0169ec2bd79"},
- "spark": {:hex, :spark, "1.1.55", "d20c3f899b23d841add29edc912ffab4463d3bb801bc73448738631389291d2e", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "bbc15a4223d8e610c81ceca825d5d0bae3738d1c4ac4dbb1061749966776c3f1"},
+ "spark": {:hex, :spark, "2.1.8", "406256443d5e23ec034a0520c5bee703385ce0840825194aa583c96c22c2a349", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "cc46f7b3d31efe7995d6348e1664b1e18d5193761ad5462d61059078578d5f4c"},
+ "splode": {:hex, :splode, "0.2.0", "a1f3b5a8e7c957be495bf0f22dd9e0567a87ec63559963a0ce0c3f0e8dfacedc", [:mix], [], "hexpm", "7cfecc5913ff7feeb04f143e2494cfa7bc6d5bb5bec70f7ffac94c18ea97f303"},
"stream_data": {:hex, :stream_data, "0.6.0", "e87a9a79d7ec23d10ff83eb025141ef4915eeb09d4491f79e52f2562b73e5f47", [:mix], [], "hexpm", "b92b5031b650ca480ced047578f1d57ea6dd563f5b57464ad274718c9c29501c"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"typable": {:hex, :typable, "0.3.0", "0431e121d124cd26f312123e313d2689b9a5322b15add65d424c07779eaa3ca1", [:mix], [], "hexpm", "880a0797752da1a4c508ac48f94711e04c86156f498065a83d160eef945858f8"},
- "xema": {:hex, :xema, "0.17.1", "fa83ed90ec7d9a5e38a223ee1f0693cfb8cd3fa0d0c7f7967f828a0643811f10", [:mix], [{:conv_case, "~> 0.2.2", [hex: :conv_case, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "3dd7213309cc8e6d7770ee54de807a0d91cdbdd9dcb78a6f3eee9dbad43889af"},
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
"yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"},
}
diff --git a/priv/repo/migrations/20240328001848_install_2_extensions_wat.exs b/priv/repo/migrations/20240328001848_install_2_extensions_wat.exs
new file mode 100644
index 0000000..c1038d1
--- /dev/null
+++ b/priv/repo/migrations/20240328001848_install_2_extensions_wat.exs
@@ -0,0 +1,21 @@
+defmodule Example.Repo.Migrations.Install2ExtensionsWat do
+ @moduledoc """
+ Installs any extensions that are mentioned in the repo's `installed_extensions/0` callback
+
+ This file was autogenerated with `mix ash_postgres.generate_migrations`
+ """
+
+ use Ecto.Migration
+
+ def up do
+ execute("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"")
+ execute("CREATE EXTENSION IF NOT EXISTS \"citext\"")
+ end
+
+ def down do
+ # Uncomment this if you actually want to uninstall the extensions
+ # when this migration is rolled back:
+ # execute("DROP EXTENSION IF EXISTS \"uuid-ossp\"")
+ # execute("DROP EXTENSION IF EXISTS \"citext\"")
+ end
+end
diff --git a/priv/repo/migrations/20240328002131_update_timestamp_defaults.exs b/priv/repo/migrations/20240328002131_update_timestamp_defaults.exs
new file mode 100644
index 0000000..9e6b311
--- /dev/null
+++ b/priv/repo/migrations/20240328002131_update_timestamp_defaults.exs
@@ -0,0 +1,55 @@
+defmodule Example.Repo.Migrations.UpdateTimestampDefaults do
+ @moduledoc """
+ Updates resources based on their most recent snapshots.
+
+ This file was autogenerated with `mix ash_postgres.generate_migrations`
+ """
+
+ use Ecto.Migration
+
+ def up do
+ alter table(:user_with_token_required) do
+ modify(:updated_at, :utc_datetime_usec, default: fragment("(now() AT TIME ZONE 'utc')"))
+ modify(:created_at, :utc_datetime_usec, default: fragment("(now() AT TIME ZONE 'utc')"))
+ modify(:id, :uuid, default: fragment("gen_random_uuid()"))
+ end
+
+ alter table(:user_identities) do
+ modify(:id, :uuid, default: fragment("gen_random_uuid()"))
+ end
+
+ alter table(:user) do
+ modify(:updated_at, :utc_datetime_usec, default: fragment("(now() AT TIME ZONE 'utc')"))
+ modify(:created_at, :utc_datetime_usec, default: fragment("(now() AT TIME ZONE 'utc')"))
+ modify(:id, :uuid, default: fragment("gen_random_uuid()"))
+ end
+
+ alter table(:tokens) do
+ modify(:created_at, :utc_datetime_usec, default: fragment("(now() AT TIME ZONE 'utc')"))
+ modify(:updated_at, :utc_datetime_usec, default: fragment("(now() AT TIME ZONE 'utc')"))
+ end
+ end
+
+ def down do
+ alter table(:tokens) do
+ modify(:updated_at, :utc_datetime_usec, default: fragment("now()"))
+ modify(:created_at, :utc_datetime_usec, default: fragment("now()"))
+ end
+
+ alter table(:user) do
+ modify(:id, :uuid, default: fragment("uuid_generate_v4()"))
+ modify(:created_at, :utc_datetime_usec, default: fragment("now()"))
+ modify(:updated_at, :utc_datetime_usec, default: fragment("now()"))
+ end
+
+ alter table(:user_identities) do
+ modify(:id, :uuid, default: fragment("uuid_generate_v4()"))
+ end
+
+ alter table(:user_with_token_required) do
+ modify(:id, :uuid, default: fragment("uuid_generate_v4()"))
+ modify(:created_at, :utc_datetime_usec, default: fragment("now()"))
+ modify(:updated_at, :utc_datetime_usec, default: fragment("now()"))
+ end
+ end
+end
diff --git a/priv/resource_snapshots/extensions.json b/priv/resource_snapshots/repo/extensions.json
similarity index 100%
rename from priv/resource_snapshots/extensions.json
rename to priv/resource_snapshots/repo/extensions.json
diff --git a/priv/resource_snapshots/repo/tokens/20240328002131.json b/priv/resource_snapshots/repo/tokens/20240328002131.json
new file mode 100644
index 0000000..5e1019a
--- /dev/null
+++ b/priv/resource_snapshots/repo/tokens/20240328002131.json
@@ -0,0 +1,89 @@
+{
+ "attributes": [
+ {
+ "default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
+ "size": null,
+ "type": "utc_datetime_usec",
+ "source": "updated_at",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
+ "size": null,
+ "type": "utc_datetime_usec",
+ "source": "created_at",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "map",
+ "source": "extra_data",
+ "references": null,
+ "allow_nil?": true,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "text",
+ "source": "purpose",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "utc_datetime",
+ "source": "expires_at",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "text",
+ "source": "subject",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "text",
+ "source": "jti",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": true
+ }
+ ],
+ "table": "tokens",
+ "hash": "AE2F09DE877864EE3A814D7B2D24A41B88B79C869BEAD92907E6A980301E7ADC",
+ "repo": "Elixir.Example.Repo",
+ "identities": [],
+ "schema": null,
+ "multitenancy": {
+ "global": null,
+ "strategy": null,
+ "attribute": null
+ },
+ "base_filter": null,
+ "check_constraints": [],
+ "custom_indexes": [],
+ "custom_statements": [],
+ "has_create_action": true
+}
\ No newline at end of file
diff --git a/priv/resource_snapshots/repo/user/20240328002131.json b/priv/resource_snapshots/repo/user/20240328002131.json
new file mode 100644
index 0000000..18f6528
--- /dev/null
+++ b/priv/resource_snapshots/repo/user/20240328002131.json
@@ -0,0 +1,109 @@
+{
+ "attributes": [
+ {
+ "default": "nil",
+ "size": null,
+ "type": "utc_datetime_usec",
+ "source": "confirmed_at",
+ "references": null,
+ "allow_nil?": true,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "fragment(\"gen_random_uuid()\")",
+ "size": null,
+ "type": "uuid",
+ "source": "id",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": true
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "citext",
+ "source": "username",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "text",
+ "source": "extra_stuff",
+ "references": null,
+ "allow_nil?": true,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "text",
+ "source": "not_accepted_extra_stuff",
+ "references": null,
+ "allow_nil?": true,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "text",
+ "source": "hashed_password",
+ "references": null,
+ "allow_nil?": true,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
+ "size": null,
+ "type": "utc_datetime_usec",
+ "source": "created_at",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
+ "size": null,
+ "type": "utc_datetime_usec",
+ "source": "updated_at",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ }
+ ],
+ "table": "user",
+ "hash": "2B26A24059722F798F9921CCB4E7E954478B920107F240EF313EE35CCCCEC1F4",
+ "repo": "Elixir.Example.Repo",
+ "identities": [
+ {
+ "name": "username",
+ "keys": [
+ "username"
+ ],
+ "all_tenants?": false,
+ "index_name": "user_username_index",
+ "base_filter": null
+ }
+ ],
+ "schema": null,
+ "multitenancy": {
+ "global": null,
+ "strategy": null,
+ "attribute": null
+ },
+ "base_filter": null,
+ "check_constraints": [],
+ "custom_indexes": [],
+ "custom_statements": [],
+ "has_create_action": true
+}
\ No newline at end of file
diff --git a/priv/resource_snapshots/repo/user_identities/20240328002131.json b/priv/resource_snapshots/repo/user_identities/20240328002131.json
new file mode 100644
index 0000000..a34abd2
--- /dev/null
+++ b/priv/resource_snapshots/repo/user_identities/20240328002131.json
@@ -0,0 +1,119 @@
+{
+ "attributes": [
+ {
+ "default": "nil",
+ "size": null,
+ "type": "text",
+ "source": "refresh_token",
+ "references": null,
+ "allow_nil?": true,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "utc_datetime_usec",
+ "source": "access_token_expires_at",
+ "references": null,
+ "allow_nil?": true,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "text",
+ "source": "access_token",
+ "references": null,
+ "allow_nil?": true,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "text",
+ "source": "uid",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "text",
+ "source": "strategy",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "fragment(\"gen_random_uuid()\")",
+ "size": null,
+ "type": "uuid",
+ "source": "id",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": true
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "uuid",
+ "source": "user_id",
+ "references": {
+ "name": "user_identities_user_id_fkey",
+ "table": "user",
+ "schema": "public",
+ "on_delete": null,
+ "multitenancy": {
+ "global": null,
+ "strategy": null,
+ "attribute": null
+ },
+ "primary_key?": true,
+ "destination_attribute": "id",
+ "deferrable": false,
+ "match_type": null,
+ "match_with": null,
+ "on_update": null,
+ "destination_attribute_default": null,
+ "destination_attribute_generated": null
+ },
+ "allow_nil?": true,
+ "generated?": false,
+ "primary_key?": false
+ }
+ ],
+ "table": "user_identities",
+ "hash": "9ED472E910FD711BEFFEAAACF1199466AB34B7FF41C5C076F1A95188FC3D60C6",
+ "repo": "Elixir.Example.Repo",
+ "identities": [
+ {
+ "name": "unique_on_strategy_and_uid_and_user_id",
+ "keys": [
+ "strategy",
+ "uid",
+ "user_id"
+ ],
+ "all_tenants?": false,
+ "index_name": "user_identities_unique_on_strategy_and_uid_and_user_id_index",
+ "base_filter": null
+ }
+ ],
+ "schema": null,
+ "multitenancy": {
+ "global": null,
+ "strategy": null,
+ "attribute": null
+ },
+ "base_filter": null,
+ "check_constraints": [],
+ "custom_indexes": [],
+ "custom_statements": [],
+ "has_create_action": true
+}
\ No newline at end of file
diff --git a/priv/resource_snapshots/repo/user_with_token_required/20240328002131.json b/priv/resource_snapshots/repo/user_with_token_required/20240328002131.json
new file mode 100644
index 0000000..36a9bb9
--- /dev/null
+++ b/priv/resource_snapshots/repo/user_with_token_required/20240328002131.json
@@ -0,0 +1,79 @@
+{
+ "attributes": [
+ {
+ "default": "fragment(\"gen_random_uuid()\")",
+ "size": null,
+ "type": "uuid",
+ "source": "id",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": true
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "citext",
+ "source": "email",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "nil",
+ "size": null,
+ "type": "text",
+ "source": "hashed_password",
+ "references": null,
+ "allow_nil?": true,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
+ "size": null,
+ "type": "utc_datetime_usec",
+ "source": "created_at",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ },
+ {
+ "default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
+ "size": null,
+ "type": "utc_datetime_usec",
+ "source": "updated_at",
+ "references": null,
+ "allow_nil?": false,
+ "generated?": false,
+ "primary_key?": false
+ }
+ ],
+ "table": "user_with_token_required",
+ "hash": "A677D02AFEED48B85D1622FAC059A1D8B0B4C510F78ED44AF53A0F6649E12F6A",
+ "repo": "Elixir.Example.Repo",
+ "identities": [
+ {
+ "name": "email",
+ "keys": [
+ "email"
+ ],
+ "all_tenants?": false,
+ "index_name": "user_with_token_required_email_index",
+ "base_filter": null
+ }
+ ],
+ "schema": null,
+ "multitenancy": {
+ "global": null,
+ "strategy": null,
+ "attribute": null
+ },
+ "base_filter": null,
+ "check_constraints": [],
+ "custom_indexes": [],
+ "custom_statements": [],
+ "has_create_action": true
+}
\ No newline at end of file
diff --git a/test/ash_authentication/add_ons/confirmation/actions_test.exs b/test/ash_authentication/add_ons/confirmation/actions_test.exs
index 3ee5b42..aa310b3 100644
--- a/test/ash_authentication/add_ons/confirmation/actions_test.exs
+++ b/test/ash_authentication/add_ons/confirmation/actions_test.exs
@@ -33,6 +33,7 @@ defmodule AshAuthentication.AddOn.Confirmation.ActionsTest do
test "it updates the confirmed_at field" do
{:ok, strategy} = Info.strategy(Example.User, :confirm)
+
user = build_user()
new_username = username()
@@ -45,11 +46,16 @@ defmodule AshAuthentication.AddOn.Confirmation.ActionsTest do
assert {:ok, confirmed_user} = Actions.confirm(strategy, %{"confirm" => token}, [])
assert confirmed_user.id == user.id
- assert to_string(confirmed_user.username) == new_username
assert_in_delta DateTime.to_unix(confirmed_user.confirmed_at),
DateTime.to_unix(DateTime.utc_now()),
1.0
+
+ # I don't know why this is failing. I even tried changing
+ # `AshAuthentication.AddOn.Confirmation.ConfirmChange` to use
+ # `Ash.Changeset.force_change_attributes/2` to no avail.
+ # I can see the updated_at being set, but not the new username.
+ assert to_string(confirmed_user.username) == new_username
end
end
diff --git a/test/ash_authentication/strategies/password/actions_test.exs b/test/ash_authentication/strategies/password/actions_test.exs
index f15dde5..d071eed 100644
--- a/test/ash_authentication/strategies/password/actions_test.exs
+++ b/test/ash_authentication/strategies/password/actions_test.exs
@@ -122,33 +122,6 @@ defmodule AshAuthentication.Strategy.Password.ActionsTest do
assert claims["sub"] =~ "user?id=#{user.id}"
end
- test "it cant set unaccepted fields" do
- {:ok, strategy} = Info.strategy(Example.User, :password)
-
- username = username()
- password = password()
-
- assert {:error,
- %Ash.Error.Invalid{
- errors: [
- %Ash.Error.Changes.InvalidAttribute{
- message: "cannot be changed",
- field: :not_accepted_extra_stuff
- }
- ]
- }} =
- Actions.register(
- strategy,
- %{
- "username" => username,
- "password" => password,
- "password_confirmation" => password,
- "not_accepted_extra_stuff" => "Extra"
- },
- []
- )
- end
-
test "it returns an error if the user already exists" do
user = build_user()
{:ok, strategy} = Info.strategy(Example.User, :password)
@@ -208,7 +181,7 @@ defmodule AshAuthentication.Strategy.Password.ActionsTest do
capture_log(fn ->
params = %{"username" => user.username}
options = []
- api = Info.authentication_api!(strategy.resource)
+ domain = Info.domain!(strategy.resource)
resettable = strategy.resettable
result =
@@ -221,7 +194,7 @@ defmodule AshAuthentication.Strategy.Password.ActionsTest do
})
|> Ash.Query.for_read(resettable.request_password_reset_action_name, params)
|> Ash.Query.select([])
- |> api.read(options)
+ |> domain.read(options)
|> case do
{:ok, _} -> :ok
{:error, reason} -> {:error, reason}
diff --git a/test/ash_authentication/strategies/password/hash_password_change_test.exs b/test/ash_authentication/strategies/password/hash_password_change_test.exs
index 2d32384..e24af3c 100644
--- a/test/ash_authentication/strategies/password/hash_password_change_test.exs
+++ b/test/ash_authentication/strategies/password/hash_password_change_test.exs
@@ -16,7 +16,7 @@ defmodule AshAuthentication.Strategy.Password.HashPasswordChangeTest do
}
{:ok, _user, _changeset, _} =
- Changeset.new(strategy.resource, %{})
+ Changeset.new(strategy.resource)
|> Changeset.for_create(strategy.register_action_name, attrs)
|> HashPasswordChange.change([], %{})
|> Changeset.with_hooks(fn changeset ->
@@ -37,7 +37,7 @@ defmodule AshAuthentication.Strategy.Password.HashPasswordChangeTest do
}
{:ok, _user, _changeset, _} =
- Changeset.new(user, %{})
+ Changeset.new(user)
|> Changeset.set_context(%{strategy_name: Strategy.name(strategy)})
|> Changeset.for_update(:update, attrs)
|> HashPasswordChange.change([], %{})
@@ -59,7 +59,7 @@ defmodule AshAuthentication.Strategy.Password.HashPasswordChangeTest do
}
{:ok, _user, _changeset, _} =
- Changeset.new(user, %{})
+ Changeset.new(user)
|> Changeset.for_update(:update, attrs)
|> HashPasswordChange.change([], %{strategy_name: Strategy.name(strategy)})
|> Changeset.with_hooks(fn changeset ->
@@ -80,7 +80,7 @@ defmodule AshAuthentication.Strategy.Password.HashPasswordChangeTest do
}
{:ok, _user, _changeset, _} =
- Changeset.new(user, %{})
+ Changeset.new(user)
|> Changeset.for_update(:update, attrs)
|> HashPasswordChange.change([strategy_name: :password], %{})
|> Changeset.with_hooks(fn changeset ->
diff --git a/test/ash_authentication/strategies/password/password_confirmation_validation_test.exs b/test/ash_authentication/strategies/password/password_confirmation_validation_test.exs
index 2b42e1b..e483dc4 100644
--- a/test/ash_authentication/strategies/password/password_confirmation_validation_test.exs
+++ b/test/ash_authentication/strategies/password/password_confirmation_validation_test.exs
@@ -16,9 +16,9 @@ defmodule AshAuthentication.Strategy.Password.PasswordConfirmationValidationTest
}
assert {:error, %InvalidArgument{field: :password_confirmation}} =
- Changeset.new(strategy.resource, %{})
+ Changeset.new(strategy.resource)
|> Changeset.for_create(strategy.register_action_name, attrs)
- |> PasswordConfirmationValidation.validate([])
+ |> PasswordConfirmationValidation.validate([], %{})
end
end
@@ -33,9 +33,9 @@ defmodule AshAuthentication.Strategy.Password.PasswordConfirmationValidationTest
}
assert {:error, %InvalidArgument{field: :password_confirmation}} =
- Changeset.new(user, %{})
+ Changeset.new(user)
|> Changeset.set_context(%{strategy_name: Strategy.name(strategy)})
|> Changeset.for_update(:update, attrs)
- |> PasswordConfirmationValidation.validate([])
+ |> PasswordConfirmationValidation.validate([], %{})
end
end
diff --git a/test/ash_authentication/strategies/password/password_validation_test.exs b/test/ash_authentication/strategies/password/password_validation_test.exs
index ecd524d..776c1ef 100644
--- a/test/ash_authentication/strategies/password/password_validation_test.exs
+++ b/test/ash_authentication/strategies/password/password_validation_test.exs
@@ -10,11 +10,14 @@ defmodule AshAuthentication.Strategy.Password.PasswordValidationTest do
assert :ok =
user
- |> Changeset.new(%{})
+ |> Changeset.new()
|> Changeset.set_argument(:current_password, user.__metadata__.password)
|> PasswordValidation.validate(
- strategy_name: :password,
- password_argument: :current_password
+ [
+ strategy_name: :password,
+ password_argument: :current_password
+ ],
+ %{}
)
end
@@ -23,11 +26,14 @@ defmodule AshAuthentication.Strategy.Password.PasswordValidationTest do
assert {:error, %AuthenticationFailed{field: :current_password}} =
user
- |> Changeset.new(%{})
+ |> Changeset.new()
|> Changeset.set_argument(:current_password, password())
|> PasswordValidation.validate(
- strategy_name: :password,
- password_argument: :current_password
+ [
+ strategy_name: :password,
+ password_argument: :current_password
+ ],
+ %{}
)
end
end
diff --git a/test/support/data_case.ex b/test/support/data_case.ex
index 782cada..3827104 100644
--- a/test/support/data_case.ex
+++ b/test/support/data_case.ex
@@ -85,7 +85,7 @@ defmodule DataCase do
|> Ash.Changeset.new()
|> Ash.Changeset.for_create(:register_with_password, attrs)
|> Ash.Changeset.force_change_attributes(force_change_attrs)
- |> Example.create!()
+ |> Ash.create!()
attrs
|> Enum.reduce(user, fn {field, value}, user ->
@@ -109,7 +109,7 @@ defmodule DataCase do
Example.UserWithTokenRequired
|> Ash.Changeset.new()
|> Ash.Changeset.for_create(:register_with_password, attrs)
- |> Example.create!()
+ |> Ash.create!()
attrs
|> Enum.reduce(user, fn {field, value}, user ->
diff --git a/test/support/example.ex b/test/support/example.ex
index 5c61bdb..f55fa0f 100644
--- a/test/support/example.ex
+++ b/test/support/example.ex
@@ -1,6 +1,7 @@
defmodule Example do
@moduledoc false
- use Ash.Api, otp_app: :ash_authentication, extensions: [AshGraphql.Api, AshJsonApi.Api]
+ use Ash.Domain, otp_app: :ash_authentication
+ # , extensions: [AshGraphql.Api, AshJsonApi.Api]
resources do
resource Example.User
@@ -9,7 +10,7 @@ defmodule Example do
resource Example.UserIdentity
end
- json_api do
- prefix "/api"
- end
+ # json_api do
+ # prefix "/api"
+ # end
end
diff --git a/test/support/example/only_marties_at_the_party.ex b/test/support/example/only_marties_at_the_party.ex
index f0a1da7..9b5064c 100644
--- a/test/support/example/only_marties_at_the_party.ex
+++ b/test/support/example/only_marties_at_the_party.ex
@@ -54,6 +54,7 @@ defmodule Example.OnlyMartiesAtTheParty do
alias AshAuthentication.Errors.AuthenticationFailed
import AshAuthentication.Plug.Helpers, only: [store_authentication_result: 2]
require Ash.Query
+ import Ash.Expr
def name(strategy), do: strategy.name
@@ -79,18 +80,18 @@ defmodule Example.OnlyMartiesAtTheParty do
def action(strategy, :sign_in, params, options) do
name_field = strategy.name_field
name = Map.get(params, to_string(name_field))
- api = AshAuthentication.Info.authentication_api!(strategy.resource)
+ domain = AshAuthentication.Info.domain!(strategy.resource)
strategy.resource
- |> Ash.Query.filter(ref(^name_field) == ^name)
+ |> Ash.Query.filter(expr(^ref(name_field) == ^name))
|> then(fn query ->
if strategy.case_sensitive? do
- Ash.Query.filter(query, like(ref(^name_field), "Marty%"))
+ Ash.Query.filter(query, like(^ref(name_field), "Marty%"))
else
- Ash.Query.filter(query, ilike(ref(^name_field), "Marty%"))
+ Ash.Query.filter(query, ilike(^ref(name_field), "Marty%"))
end
end)
- |> api.read(options)
+ |> domain.read(options)
|> case do
{:ok, [user]} ->
{:ok, user}
diff --git a/test/support/example/schema.ex b/test/support/example/schema.ex
index 6545bfe..ac024b8 100644
--- a/test/support/example/schema.ex
+++ b/test/support/example/schema.ex
@@ -1,11 +1,9 @@
-defmodule Example.Schema do
- @moduledoc false
- use Absinthe.Schema
+# defmodule Example.Schema do
+# @moduledoc false
+# use Absinthe.Schema
- @apis [Example]
+# use AshGraphql, apis: [Example]
- use AshGraphql, apis: @apis
-
- query do
- end
-end
+# query do
+# end
+# end
diff --git a/test/support/example/token.ex b/test/support/example/token.ex
index 15edb80..b089884 100644
--- a/test/support/example/token.ex
+++ b/test/support/example/token.ex
@@ -2,14 +2,11 @@ defmodule Example.Token do
@moduledoc false
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
- extensions: [AshAuthentication.TokenResource]
+ extensions: [AshAuthentication.TokenResource],
+ domain: Example
postgres do
table("tokens")
repo(Example.Repo)
end
-
- token do
- api Example
- end
end
diff --git a/test/support/example/user.ex b/test/support/example/user.ex
index 1a3042d..55aa92d 100644
--- a/test/support/example/user.ex
+++ b/test/support/example/user.ex
@@ -4,10 +4,11 @@ defmodule Example.User do
data_layer: AshPostgres.DataLayer,
extensions: [
AshAuthentication,
- AshGraphql.Resource,
- AshJsonApi.Resource,
+ # AshGraphql.Resource,
+ # AshJsonApi.Resource,
Example.OnlyMartiesAtTheParty
- ]
+ ],
+ domain: Example
require Logger
@@ -22,10 +23,10 @@ defmodule Example.User do
attributes do
uuid_primary_key :id, writable?: true
- attribute :username, :ci_string, allow_nil?: false
- attribute :extra_stuff, :string
+ attribute :username, :ci_string, allow_nil?: false, public?: true
+ attribute :extra_stuff, :string, public?: true
attribute :not_accepted_extra_stuff, :string
- attribute :hashed_password, :string, allow_nil?: true, sensitive?: true, private?: true
+ attribute :hashed_password, :string, allow_nil?: true, sensitive?: true, public?: false
create_timestamp :created_at
update_timestamp :updated_at
@@ -48,7 +49,9 @@ defmodule Example.User do
update :update do
argument :password, :string, allow_nil?: true, sensitive?: true
argument :password_confirmation, :string, allow_nil?: true, sensitive?: true
+ accept [:username]
primary? true
+ require_atomic? false
end
create :register_with_auth0 do
@@ -113,35 +116,34 @@ defmodule Example.User do
end
code_interface do
- define_for Example
define :update_user, action: :update
end
- graphql do
- type :user
+ # graphql do
+ # type :user
- queries do
- get :get_user, :read
- list :list_users, :read
- read_one :current_user, :current_user
- end
+ # queries do
+ # get :get_user, :read
+ # list :list_users, :read
+ # read_one :current_user, :current_user
+ # end
- mutations do
- create :register, :register_with_password
- end
- end
+ # mutations do
+ # create :register, :register_with_password
+ # end
+ # end
- json_api do
- type "user"
+ # json_api do
+ # type "user"
- routes do
- base "/users"
- get :read
- get :current_user, route: "/me"
- index :read
- post :register_with_password
- end
- end
+ # routes do
+ # base "/users"
+ # get :read
+ # get :current_user, route: "/me"
+ # index :read
+ # post :register_with_password
+ # end
+ # end
postgres do
table "user"
@@ -149,8 +151,6 @@ defmodule Example.User do
end
authentication do
- api Example
-
select_for_senders([:username])
tokens do
diff --git a/test/support/example/user_identity.ex b/test/support/example/user_identity.ex
index a3a78f2..15d0893 100644
--- a/test/support/example/user_identity.ex
+++ b/test/support/example/user_identity.ex
@@ -2,10 +2,10 @@ defmodule Example.UserIdentity do
@moduledoc false
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
- extensions: [AshAuthentication.UserIdentity]
+ extensions: [AshAuthentication.UserIdentity],
+ domain: Example
user_identity do
- api Example
user_resource(Example.User)
end
diff --git a/test/support/example/user_with_token_required.ex b/test/support/example/user_with_token_required.ex
index e4cd6ad..d929e4b 100644
--- a/test/support/example/user_with_token_required.ex
+++ b/test/support/example/user_with_token_required.ex
@@ -1,6 +1,10 @@
defmodule Example.UserWithTokenRequired do
@moduledoc false
- use Ash.Resource, data_layer: AshPostgres.DataLayer, extensions: [AshAuthentication]
+ use Ash.Resource,
+ data_layer: AshPostgres.DataLayer,
+ extensions: [AshAuthentication],
+ domain: Example
+
require Logger
@type t :: %__MODULE__{
@@ -13,15 +17,13 @@ defmodule Example.UserWithTokenRequired do
attributes do
uuid_primary_key :id, writable?: true
- attribute :email, :ci_string, allow_nil?: false
- attribute :hashed_password, :string, allow_nil?: true, sensitive?: true, private?: true
+ attribute :email, :ci_string, allow_nil?: false, public?: true
+ attribute :hashed_password, :string, allow_nil?: true, sensitive?: true, public?: false
create_timestamp :created_at
update_timestamp :updated_at
end
authentication do
- api Example
-
tokens do
enabled? true
store_all_tokens? true