mirror of
https://github.com/team-alembic/ash_authentication.git
synced 2024-09-19 04:43:04 +12:00
feat!: Configure accepted fields on register (#219)
This commit is contained in:
parent
668538be59
commit
7f1c9678e4
11 changed files with 243 additions and 2 deletions
|
@ -1,4 +1,6 @@
|
|||
spark_locals_without_parens = [
|
||||
access_token_attribute_name: 1,
|
||||
access_token_expires_at_attribute_name: 1,
|
||||
api: 1,
|
||||
auth0: 0,
|
||||
auth0: 1,
|
||||
|
@ -16,12 +18,16 @@ spark_locals_without_parens = [
|
|||
confirmation: 2,
|
||||
confirmation_required?: 1,
|
||||
confirmed_at_field: 1,
|
||||
destroy_action_name: 1,
|
||||
enabled?: 1,
|
||||
expunge_expired_action_name: 1,
|
||||
expunge_interval: 1,
|
||||
get_by_subject_action_name: 1,
|
||||
get_changes_action_name: 1,
|
||||
get_token_action_name: 1,
|
||||
github: 0,
|
||||
github: 1,
|
||||
github: 2,
|
||||
hash_provider: 1,
|
||||
hashed_password_field: 1,
|
||||
identity_field: 1,
|
||||
|
@ -30,6 +36,9 @@ spark_locals_without_parens = [
|
|||
identity_resource: 1,
|
||||
inhibit_updates?: 1,
|
||||
is_revoked_action_name: 1,
|
||||
magic_link: 0,
|
||||
magic_link: 1,
|
||||
magic_link: 2,
|
||||
monitor_fields: 1,
|
||||
oauth2: 0,
|
||||
oauth2: 1,
|
||||
|
@ -41,27 +50,41 @@ spark_locals_without_parens = [
|
|||
password_field: 1,
|
||||
password_reset_action_name: 1,
|
||||
private_key: 1,
|
||||
read_action_name: 1,
|
||||
read_expired_action_name: 1,
|
||||
redirect_uri: 1,
|
||||
refresh_token_attribute_name: 1,
|
||||
register_action_accept: 1,
|
||||
register_action_name: 1,
|
||||
registration_enabled?: 1,
|
||||
request_action_name: 1,
|
||||
request_password_reset_action_name: 1,
|
||||
require_token_presence_for_authentication?: 1,
|
||||
resettable: 0,
|
||||
resettable: 1,
|
||||
revoke_token_action_name: 1,
|
||||
select_for_senders: 1,
|
||||
sender: 1,
|
||||
sign_in_action_name: 1,
|
||||
sign_in_enabled?: 1,
|
||||
signing_algorithm: 1,
|
||||
signing_secret: 1,
|
||||
single_use_token?: 1,
|
||||
site: 1,
|
||||
store_all_tokens?: 1,
|
||||
store_changes_action_name: 1,
|
||||
store_token_action_name: 1,
|
||||
strategy_attribute_name: 1,
|
||||
subject_name: 1,
|
||||
token_lifetime: 1,
|
||||
token_url: 1,
|
||||
token_param_name: 1,
|
||||
token_resource: 1,
|
||||
token_url: 1,
|
||||
uid_attribute_name: 1,
|
||||
upsert_action_name: 1,
|
||||
user_id_attribute_name: 1,
|
||||
user_relationship_name: 1,
|
||||
user_resource: 1,
|
||||
user_url: 1
|
||||
]
|
||||
|
||||
|
|
12
.github/workflows/elixir_lib.yml
vendored
12
.github/workflows/elixir_lib.yml
vendored
|
@ -40,6 +40,17 @@ jobs:
|
|||
with:
|
||||
mix-env: test
|
||||
|
||||
spark-formatter:
|
||||
name: mix spark.formatter --check
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-test
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: team-alembic/staple-actions/actions/mix-task@main
|
||||
with:
|
||||
mix-env: test
|
||||
task: spark.formatter --check
|
||||
|
||||
credo:
|
||||
name: mix credo --strict
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -113,6 +124,7 @@ jobs:
|
|||
- credo
|
||||
- doctor
|
||||
- formatter
|
||||
- spark-formatter
|
||||
- auditor
|
||||
- test
|
||||
- dialyzer
|
||||
|
|
|
@ -106,6 +106,7 @@ defmodule AshAuthentication.Strategy.Password do
|
|||
registration_enabled?: true,
|
||||
sign_in_enabled?: true,
|
||||
resettable: [],
|
||||
register_action_accept: [],
|
||||
name: nil,
|
||||
provider: :password,
|
||||
resource: nil
|
||||
|
@ -130,6 +131,7 @@ defmodule AshAuthentication.Strategy.Password do
|
|||
confirmation_required?: boolean,
|
||||
password_field: atom,
|
||||
password_confirmation_field: atom,
|
||||
register_action_accept: [atom],
|
||||
register_action_name: atom,
|
||||
sign_in_action_name: atom,
|
||||
registration_enabled?: boolean,
|
||||
|
|
|
@ -72,6 +72,11 @@ defmodule AshAuthentication.Strategy.Password.Dsl do
|
|||
""",
|
||||
default: true
|
||||
],
|
||||
register_action_accept: [
|
||||
type: {:list, :atom},
|
||||
default: [],
|
||||
doc: "A list of additional fields to be accepted in the register action."
|
||||
],
|
||||
password_field: [
|
||||
type: :atom,
|
||||
doc: """
|
||||
|
|
|
@ -144,8 +144,14 @@ defmodule AshAuthentication.Strategy.Password.Transformer do
|
|||
[]
|
||||
end
|
||||
|
||||
accept =
|
||||
[strategy.identity_field]
|
||||
|> Enum.concat(List.wrap(strategy.register_action_accept))
|
||||
|> Enum.uniq()
|
||||
|
||||
Transformer.build_entity(Resource.Dsl, [:actions], :create,
|
||||
name: strategy.register_action_name,
|
||||
accept: accept,
|
||||
arguments: arguments,
|
||||
changes: changes,
|
||||
metadata: metadata,
|
||||
|
|
3
mix.exs
3
mix.exs
|
@ -179,6 +179,9 @@ defmodule AshAuthentication.MixProject do
|
|||
"hex.audit",
|
||||
"test"
|
||||
],
|
||||
"spark.formatter": [
|
||||
"spark.formatter --extensions AshAuthentication,AshAuthentication.TokenResource,AshAuthentication.UserIdentity,AshAuthentication.Strategy.MagicLink,AshAuthentication.AddOn.Confirmation,AshAuthentication.Strategy.Auth0,AshAuthentication.Strategy.Github,AshAuthentication.Strategy.OAuth2,AshAuthentication.Strategy.Password"
|
||||
],
|
||||
docs: ["docs", "ash.replace_doc_links"],
|
||||
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"]
|
||||
]
|
||||
|
|
23
priv/repo/migrations/20230302020116_migrate_resources2.exs
Normal file
23
priv/repo/migrations/20230302020116_migrate_resources2.exs
Normal file
|
@ -0,0 +1,23 @@
|
|||
defmodule Example.Repo.Migrations.MigrateResources2 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) do
|
||||
add :extra_stuff, :text
|
||||
add :not_accepted_extra_stuff, :text
|
||||
end
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:user) do
|
||||
remove :not_accepted_extra_stuff
|
||||
remove :extra_stuff
|
||||
end
|
||||
end
|
||||
end
|
108
priv/resource_snapshots/repo/user/20230302020116.json
Normal file
108
priv/resource_snapshots/repo/user/20230302020116.json
Normal file
|
@ -0,0 +1,108 @@
|
|||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "confirmed_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"uuid_generate_v4()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "username",
|
||||
"type": "citext"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "extra_stuff",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "not_accepted_extra_stuff",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "hashed_password",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"now()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "created_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"now()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "D59416503FF032F48041F4E1793CD42EE01FF6AB3D5C4FF4A9D9CABA836E96C1",
|
||||
"identities": [
|
||||
{
|
||||
"base_filter": null,
|
||||
"index_name": "user_username_index",
|
||||
"keys": [
|
||||
"username"
|
||||
],
|
||||
"name": "username"
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.Example.Repo",
|
||||
"schema": null,
|
||||
"table": "user"
|
||||
}
|
|
@ -78,6 +78,59 @@ defmodule AshAuthentication.Strategy.Password.ActionsTest do
|
|||
assert claims["sub"] =~ "user?id=#{user.id}"
|
||||
end
|
||||
|
||||
test "it can register a new user with additional accepted fields" do
|
||||
{:ok, strategy} = Info.strategy(Example.User, :password)
|
||||
|
||||
username = username()
|
||||
password = password()
|
||||
|
||||
assert {:ok, user} =
|
||||
Actions.register(
|
||||
strategy,
|
||||
%{
|
||||
"username" => username,
|
||||
"password" => password,
|
||||
"password_confirmation" => password,
|
||||
"extra_stuff" => "Extra"
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
assert user.extra_stuff == "Extra"
|
||||
|
||||
assert strategy.hash_provider.valid?(password, user.hashed_password)
|
||||
|
||||
assert {:ok, claims} = Jwt.peek(user.__metadata__.token)
|
||||
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)
|
||||
|
|
|
@ -72,17 +72,19 @@ defmodule DataCase do
|
|||
def build_user(attrs \\ []) do
|
||||
password = password()
|
||||
|
||||
attrs =
|
||||
{force_change_attrs, attrs} =
|
||||
attrs
|
||||
|> Map.new()
|
||||
|> Map.put_new(:username, username())
|
||||
|> Map.put_new(:password, password)
|
||||
|> Map.put_new(:password_confirmation, password)
|
||||
|> Map.split([:id])
|
||||
|
||||
user =
|
||||
Example.User
|
||||
|> Ash.Changeset.new()
|
||||
|> Ash.Changeset.for_create(:register_with_password, attrs)
|
||||
|> Ash.Changeset.force_change_attributes(force_change_attrs)
|
||||
|> Example.create!()
|
||||
|
||||
attrs
|
||||
|
|
|
@ -23,6 +23,8 @@ defmodule Example.User do
|
|||
uuid_primary_key :id, writable?: true
|
||||
|
||||
attribute :username, :ci_string, allow_nil?: false
|
||||
attribute :extra_stuff, :string
|
||||
attribute :not_accepted_extra_stuff, :string
|
||||
attribute :hashed_password, :string, allow_nil?: true, sensitive?: true, private?: true
|
||||
|
||||
create_timestamp :created_at
|
||||
|
@ -165,6 +167,8 @@ defmodule Example.User do
|
|||
|
||||
strategies do
|
||||
password do
|
||||
register_action_accept [:extra_stuff]
|
||||
|
||||
resettable do
|
||||
sender fn user, token, _opts ->
|
||||
Logger.debug(
|
||||
|
|
Loading…
Reference in a new issue