mirror of
https://github.com/danbee/chess
synced 2025-03-04 08:39:06 +00:00
WIP: Authentication with Guardian/Comeonin
This commit is contained in:
parent
87fa2b9f0f
commit
80e58f765f
114
lib/chess/auth/auth.ex
Normal file
114
lib/chess/auth/auth.ex
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
defmodule Chess.Auth do
|
||||||
|
@moduledoc """
|
||||||
|
The Auth context.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import Ecto.Query, warn: false
|
||||||
|
alias Comeonin.Argon2
|
||||||
|
alias Chess.Repo
|
||||||
|
|
||||||
|
alias Chess.Auth.User
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns the list of users.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> list_users()
|
||||||
|
[%User{}, ...]
|
||||||
|
|
||||||
|
"""
|
||||||
|
def list_users do
|
||||||
|
Repo.all(User)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets a single user.
|
||||||
|
|
||||||
|
Raises `Ecto.NoResultsError` if the User does not exist.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> get_user!(123)
|
||||||
|
%User{}
|
||||||
|
|
||||||
|
iex> get_user!(456)
|
||||||
|
** (Ecto.NoResultsError)
|
||||||
|
|
||||||
|
"""
|
||||||
|
def get_user!(id), do: Repo.get!(User, id)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Creates a user.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> create_user(%{field: value})
|
||||||
|
{:ok, %User{}}
|
||||||
|
|
||||||
|
iex> create_user(%{field: bad_value})
|
||||||
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def create_user(attrs \\ %{}) do
|
||||||
|
%User{}
|
||||||
|
|> User.changeset(attrs)
|
||||||
|
|> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Updates a user.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> update_user(user, %{field: new_value})
|
||||||
|
{:ok, %User{}}
|
||||||
|
|
||||||
|
iex> update_user(user, %{field: bad_value})
|
||||||
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def update_user(%User{} = user, attrs) do
|
||||||
|
user
|
||||||
|
|> User.changeset(attrs)
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Deletes a User.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> delete_user(user)
|
||||||
|
{:ok, %User{}}
|
||||||
|
|
||||||
|
iex> delete_user(user)
|
||||||
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def delete_user(%User{} = user) do
|
||||||
|
Repo.delete(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns an `%Ecto.Changeset{}` for tracking user changes.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> change_user(user)
|
||||||
|
%Ecto.Changeset{source: %User{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def change_user(%User{} = user) do
|
||||||
|
User.changeset(user, %{})
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def authenticate_user(username, password) do
|
||||||
|
Repo.one(
|
||||||
|
from u in User,
|
||||||
|
where: u.username == ^username
|
||||||
|
)
|
||||||
|
|> Argon2.check_pass(password)
|
||||||
|
end
|
||||||
|
end
|
||||||
17
lib/chess/auth/guardian.ex
Normal file
17
lib/chess/auth/guardian.ex
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
defmodule Chess.Auth.Guardian do
|
||||||
|
use Guardian, otp_app: :auth_ex
|
||||||
|
|
||||||
|
alias Chess.Auth
|
||||||
|
|
||||||
|
def subject_for_token(user, _claims) do
|
||||||
|
{:ok, to_string(user.id)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def resource_from_claims(claims) do
|
||||||
|
user = claims["sub"]
|
||||||
|
|> Auth.get_user!
|
||||||
|
{:ok, user}
|
||||||
|
|
||||||
|
# If something goes wrong here return {:error, reason}
|
||||||
|
end
|
||||||
|
end
|
||||||
32
lib/chess/auth/user.ex
Normal file
32
lib/chess/auth/user.ex
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
defmodule Chess.Auth.User do
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
use Ecto.Schema
|
||||||
|
import Ecto.Changeset
|
||||||
|
alias Chess.Auth.User
|
||||||
|
alias Comeonin.Argon2
|
||||||
|
|
||||||
|
schema "users" do
|
||||||
|
field :password, :string, virtual: true
|
||||||
|
field :password_hash, :string
|
||||||
|
field :username, :string
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def changeset(%User{} = user, attrs) do
|
||||||
|
user
|
||||||
|
|> cast(attrs, [:username, :password])
|
||||||
|
|> validate_required([:username, :password])
|
||||||
|
|> hash_password()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp hash_password(
|
||||||
|
%Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset
|
||||||
|
) do
|
||||||
|
change(changeset, Argon2.add_hash(password))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp hash_password(changeset), do: changeset
|
||||||
|
end
|
||||||
12
priv/repo/migrations/20180115230143_create_users.exs
Normal file
12
priv/repo/migrations/20180115230143_create_users.exs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
defmodule Chess.Repo.Migrations.CreateUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table(:users) do
|
||||||
|
add :username, :string
|
||||||
|
add :password_hash, :string
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
65
test/chess/auth/auth_test.exs
Normal file
65
test/chess/auth/auth_test.exs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
defmodule Chess.AuthTest do
|
||||||
|
use Chess.DataCase
|
||||||
|
|
||||||
|
alias Chess.Auth
|
||||||
|
|
||||||
|
describe "users" do
|
||||||
|
alias Chess.Auth.User
|
||||||
|
|
||||||
|
@valid_attrs %{password: "some password", username: "some username"}
|
||||||
|
@update_attrs %{password: "some updated password", username: "some updated username"}
|
||||||
|
@invalid_attrs %{password: nil, username: nil}
|
||||||
|
|
||||||
|
def user_fixture(attrs \\ %{}) do
|
||||||
|
{:ok, user} =
|
||||||
|
attrs
|
||||||
|
|> Enum.into(@valid_attrs)
|
||||||
|
|> Auth.create_user()
|
||||||
|
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
|
test "list_users/0 returns all users" do
|
||||||
|
user = user_fixture()
|
||||||
|
assert Auth.list_users() == [user]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "get_user!/1 returns the user with given id" do
|
||||||
|
user = user_fixture()
|
||||||
|
assert Auth.get_user!(user.id) == user
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create_user/1 with valid data creates a user" do
|
||||||
|
assert {:ok, %User{} = user} = Auth.create_user(@valid_attrs)
|
||||||
|
assert user.username == "some username"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create_user/1 with invalid data returns error changeset" do
|
||||||
|
assert {:error, %Ecto.Changeset{}} = Auth.create_user(@invalid_attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update_user/2 with valid data updates the user" do
|
||||||
|
user = user_fixture()
|
||||||
|
assert {:ok, user} = Auth.update_user(user, @update_attrs)
|
||||||
|
assert %User{} = user
|
||||||
|
assert user.username == "some updated username"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update_user/2 with invalid data returns error changeset" do
|
||||||
|
user = user_fixture()
|
||||||
|
assert {:error, %Ecto.Changeset{}} = Auth.update_user(user, @invalid_attrs)
|
||||||
|
assert user == Auth.get_user!(user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "delete_user/1 deletes the user" do
|
||||||
|
user = user_fixture()
|
||||||
|
assert {:ok, %User{}} = Auth.delete_user(user)
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Auth.get_user!(user.id) end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "change_user/1 returns a user changeset" do
|
||||||
|
user = user_fixture()
|
||||||
|
assert %Ecto.Changeset{} = Auth.change_user(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in New Issue
Block a user