diff --git a/apps/fg_http/lib/fg_http/password_resets.ex b/apps/fg_http/lib/fg_http/password_resets.ex
new file mode 100644
index 000000000..6093b786f
--- /dev/null
+++ b/apps/fg_http/lib/fg_http/password_resets.ex
@@ -0,0 +1,104 @@
+defmodule FgHttp.PasswordResets do
+ @moduledoc """
+ The PasswordResets context.
+ """
+
+ import Ecto.Query, warn: false
+ alias FgHttp.Repo
+
+ alias FgHttp.PasswordResets.PasswordReset
+
+ @doc """
+ Returns the list of password_resets.
+
+ ## Examples
+
+ iex> list_password_resets()
+ [%PasswordReset{}, ...]
+
+ """
+ def list_password_resets do
+ Repo.all(PasswordReset)
+ end
+
+ @doc """
+ Gets a single password_reset.
+
+ Raises `Ecto.NoResultsError` if the Password reset does not exist.
+
+ ## Examples
+
+ iex> get_password_reset!(123)
+ %PasswordReset{}
+
+ iex> get_password_reset!(456)
+ ** (Ecto.NoResultsError)
+
+ """
+ def get_password_reset!(id), do: Repo.get!(PasswordReset, id)
+
+ @doc """
+ Creates a password_reset.
+
+ ## Examples
+
+ iex> create_password_reset(%{field: value})
+ {:ok, %PasswordReset{}}
+
+ iex> create_password_reset(%{field: bad_value})
+ {:error, %Ecto.Changeset{}}
+
+ """
+ def create_password_reset(attrs \\ %{}) do
+ %PasswordReset{}
+ |> PasswordReset.create_changeset(attrs)
+ |> Repo.insert()
+ end
+
+ @doc """
+ Updates a password_reset.
+
+ ## Examples
+
+ iex> update_password_reset(password_reset, %{field: new_value})
+ {:ok, %PasswordReset{}}
+
+ iex> update_password_reset(password_reset, %{field: bad_value})
+ {:error, %Ecto.Changeset{}}
+
+ """
+ def update_password_reset(%PasswordReset{} = password_reset, attrs) do
+ password_reset
+ |> PasswordReset.update_changeset(attrs)
+ |> Repo.update()
+ end
+
+ @doc """
+ Deletes a password_reset.
+
+ ## Examples
+
+ iex> delete_password_reset(password_reset)
+ {:ok, %PasswordReset{}}
+
+ iex> delete_password_reset(password_reset)
+ {:error, %Ecto.Changeset{}}
+
+ """
+ def delete_password_reset(%PasswordReset{} = password_reset) do
+ Repo.delete(password_reset)
+ end
+
+ @doc """
+ Returns an `%Ecto.Changeset{}` for tracking password_reset changes.
+
+ ## Examples
+
+ iex> change_password_reset(password_reset)
+ %Ecto.Changeset{data: %PasswordReset{}}
+
+ """
+ def change_password_reset(%PasswordReset{} = password_reset, attrs \\ %{}) do
+ PasswordReset.changeset(password_reset, attrs)
+ end
+end
diff --git a/apps/fg_http/lib/fg_http/password_resets/password_reset.ex b/apps/fg_http/lib/fg_http/password_resets/password_reset.ex
new file mode 100644
index 000000000..fe9ad877c
--- /dev/null
+++ b/apps/fg_http/lib/fg_http/password_resets/password_reset.ex
@@ -0,0 +1,64 @@
+defmodule FgHttp.PasswordResets.PasswordReset do
+ @moduledoc """
+ Schema for PasswordReset
+ """
+
+ use Ecto.Schema
+ import Ecto.Changeset
+
+ alias FgHttp.{Users, Users.User}
+
+ @token_num_bytes 8
+
+ schema "password_resets" do
+ field :reset_sent_at, :utc_datetime
+ field :reset_token, :string
+ field :user_email, :string, virtual: true
+ belongs_to :user, User
+
+ timestamps()
+ end
+
+ @doc false
+ def changeset(password_reset, attrs) do
+ password_reset
+ |> cast(attrs, [:user_id, :user_email, :reset_sent_at, :reset_token])
+ end
+
+ @doc false
+ def create_changeset(password_reset, attrs) do
+ password_reset
+ |> cast(attrs, [:user_id, :user_email, :reset_sent_at, :reset_token])
+ |> load_user_from_email()
+ |> generate_reset_token()
+ |> validate_required([:reset_token, :user_id])
+ |> unique_constraint(:reset_token)
+ end
+
+ @doc false
+ def update_changeset(password_reset, attrs) do
+ password_reset
+ |> cast(attrs, [:user_id, :user_email, :reset_sent_at, :reset_token])
+ |> validate_required([:reset_token])
+ end
+
+ defp load_user_from_email(
+ %Ecto.Changeset{
+ valid?: true,
+ changes: %{user_email: user_email}
+ } = changeset
+ ) do
+ user = Users.get_user!(email: user_email)
+ put_change(changeset, :user_id, user.id)
+ end
+
+ defp load_user_from_email(changeset), do: changeset
+
+ defp generate_reset_token(%Ecto.Changeset{valid?: true} = changeset) do
+ random_bytes = :crypto.strong_rand_bytes(@token_num_bytes)
+ random_string = Base.url_encode64(random_bytes)
+ put_change(changeset, :reset_token, random_string)
+ end
+
+ defp generate_reset_token(changeset), do: changeset
+end
diff --git a/apps/fg_http/lib/fg_http/users.ex b/apps/fg_http/lib/fg_http/users.ex
index 323dd66b3..08f0a2b85 100644
--- a/apps/fg_http/lib/fg_http/users.ex
+++ b/apps/fg_http/lib/fg_http/users.ex
@@ -39,10 +39,6 @@ defmodule FgHttp.Users do
Repo.get_by!(User, email: email)
end
- def get_user!(reset_token: reset_token) do
- Repo.get_by!(User, reset_token: reset_token)
- end
-
def get_user!(id), do: Repo.get!(User, id)
@doc """
diff --git a/apps/fg_http/lib/fg_http/users/user.ex b/apps/fg_http/lib/fg_http/users/user.ex
index b01f6f987..9e682f9e3 100644
--- a/apps/fg_http/lib/fg_http/users/user.ex
+++ b/apps/fg_http/lib/fg_http/users/user.ex
@@ -11,8 +11,6 @@ defmodule FgHttp.Users.User do
schema "users" do
field :email, :string
field :confirmed_at, :utc_datetime
- field :reset_sent_at, :utc_datetime
- field :reset_token, :string
field :last_signed_in_at, :utc_datetime
field :password_hash, :string
diff --git a/apps/fg_http/lib/fg_http_web/controllers/password_reset_controller.ex b/apps/fg_http/lib/fg_http_web/controllers/password_reset_controller.ex
index f5531b7db..d686cdf61 100644
--- a/apps/fg_http/lib/fg_http_web/controllers/password_reset_controller.ex
+++ b/apps/fg_http/lib/fg_http_web/controllers/password_reset_controller.ex
@@ -4,35 +4,28 @@ defmodule FgHttpWeb.PasswordResetController do
"""
use FgHttpWeb, :controller
- alias FgHttp.{Users, Users.User}
+ alias FgHttp.{PasswordResets, PasswordResets.PasswordReset}
plug FgHttpWeb.Plugs.RedirectAuthenticated
def new(conn, _params) do
+ changeset = PasswordReset.changeset(%PasswordReset{}, %{})
+
conn
- |> render("new.html", changeset: User.changeset(%User{}))
+ |> render("new.html", changeset: changeset)
end
- # Don't actually create anything. Instead, update the user with a reset token and send
- # the password reset email.
- def create(conn, %{
- "password_reset" =>
- %{
- reset_token: reset_token,
- password: _password,
- password_confirmation: _password_confirmation,
- current_password: _current_password
- } = user_params
- }) do
- user = Users.get_user!(reset_token: reset_token)
-
- case Users.update_user(user, user_params) do
- {:ok, _user} ->
+ def create(conn, %{"password_reset" => %{"user_email" => _} = password_reset_params}) do
+ case PasswordResets.create_password_reset(password_reset_params) do
+ {:ok, _password_reset} ->
conn
- |> render("success.html")
+ |> clear_session()
+ |> put_flash(:info, "Password reset successfully. Please sign in with your new password.")
+ |> redirect(to: Routes.session_path(conn, :new))
{:error, changeset} ->
conn
+ |> put_flash(:error, "Error creating password reset.")
|> render("new.html", changeset: changeset)
end
end
diff --git a/apps/fg_http/lib/fg_http_web/router.ex b/apps/fg_http/lib/fg_http_web/router.ex
index a2c7a33d0..7594b7851 100644
--- a/apps/fg_http/lib/fg_http_web/router.ex
+++ b/apps/fg_http/lib/fg_http_web/router.ex
@@ -20,6 +20,8 @@ defmodule FgHttpWeb.Router do
scope "/", FgHttpWeb do
pipe_through :browser
+ resources "/password_resets", PasswordResetController, only: [:new, :create]
+
resources "/user", UserController, singleton: true, only: [:show, :edit, :update, :delete]
resources "/users", UserController, only: [:new, :create]
diff --git a/apps/fg_http/lib/fg_http_web/templates/layout/app.html.eex b/apps/fg_http/lib/fg_http_web/templates/layout/app.html.eex
index a4f9211f2..570eb2794 100644
--- a/apps/fg_http/lib/fg_http_web/templates/layout/app.html.eex
+++ b/apps/fg_http/lib/fg_http_web/templates/layout/app.html.eex
@@ -14,16 +14,17 @@
<%= link("Fireguard", to: FgHttpWeb.Endpoint.url(), class: "link dim white dib mr3") %>
- <%= if @user_signed_in? do %>
-
+
+ <%= if @user_signed_in? do %>
<%= link("Devices", to: Routes.device_path(@conn, :index), class: "link dim white dib mr3") %>
<%= link("Sign out", to: Routes.session_path(@conn, :delete, @current_session), method: :delete, class: "link dim white dib mr3") %>
-
- <% else %>
-
- <% end %>
+ <% else %>
+ <%= link("Sign in", to: Routes.session_path(@conn, :new), class: "link dim white dib mr3") %>
+ <% end %>
+
+ <%= render_flash(@conn) %>
<%= @inner_content %>
diff --git a/apps/fg_http/lib/fg_http_web/templates/password_reset/edit.html.eex b/apps/fg_http/lib/fg_http_web/templates/password_reset/edit.html.eex
new file mode 100644
index 000000000..ed57b0cd4
--- /dev/null
+++ b/apps/fg_http/lib/fg_http_web/templates/password_reset/edit.html.eex
@@ -0,0 +1,5 @@
+
Edit Password reset
+
+<%= render "form.html", Map.put(assigns, :action, Routes.password_reset_path(@conn, :update, @password_reset)) %>
+
+<%= link "Back", to: Routes.password_reset_path(@conn, :index) %>
diff --git a/apps/fg_http/lib/fg_http_web/templates/password_reset/form.html.eex b/apps/fg_http/lib/fg_http_web/templates/password_reset/form.html.eex
new file mode 100644
index 000000000..23bcc3e43
--- /dev/null
+++ b/apps/fg_http/lib/fg_http_web/templates/password_reset/form.html.eex
@@ -0,0 +1,19 @@
+<%= form_for @changeset, @action, fn f -> %>
+ <%= if @changeset.action do %>
+
+
Oops, something went wrong! Please check the errors below.
+
+ <% end %>
+
+ <%= label f, :reset_sent_at %>
+ <%= datetime_select f, :reset_sent_at %>
+ <%= error_tag f, :reset_sent_at %>
+
+ <%= label f, :reset_token %>
+ <%= text_input f, :reset_token %>
+ <%= error_tag f, :reset_token %>
+
+
+ <%= submit "Save" %>
+
+<% end %>
diff --git a/apps/fg_http/lib/fg_http_web/templates/password_reset/index.html.eex b/apps/fg_http/lib/fg_http_web/templates/password_reset/index.html.eex
new file mode 100644
index 000000000..1139f045f
--- /dev/null
+++ b/apps/fg_http/lib/fg_http_web/templates/password_reset/index.html.eex
@@ -0,0 +1,28 @@
+Listing Password resets
+
+
+
+
+ Reset sent at
+ Reset token
+
+
+
+
+
+<%= for password_reset <- @password_resets do %>
+
+ <%= password_reset.reset_sent_at %>
+ <%= password_reset.reset_token %>
+
+
+ <%= link "Show", to: Routes.password_reset_path(@conn, :show, password_reset) %>
+ <%= link "Edit", to: Routes.password_reset_path(@conn, :edit, password_reset) %>
+ <%= link "Delete", to: Routes.password_reset_path(@conn, :delete, password_reset), method: :delete, data: [confirm: "Are you sure?"] %>
+
+
+<% end %>
+
+
+
+<%= link "New Password reset", to: Routes.password_reset_path(@conn, :new) %>
diff --git a/apps/fg_http/lib/fg_http_web/templates/password_reset/new.html.eex b/apps/fg_http/lib/fg_http_web/templates/password_reset/new.html.eex
new file mode 100644
index 000000000..5181f4870
--- /dev/null
+++ b/apps/fg_http/lib/fg_http_web/templates/password_reset/new.html.eex
@@ -0,0 +1,5 @@
+New Password reset
+
+<%= render "form.html", Map.put(assigns, :action, Routes.password_reset_path(@conn, :create)) %>
+
+<%= link "Back", to: Routes.session_path(@conn, :new) %>
diff --git a/apps/fg_http/lib/fg_http_web/templates/password_reset/show.html.eex b/apps/fg_http/lib/fg_http_web/templates/password_reset/show.html.eex
new file mode 100644
index 000000000..a3470a023
--- /dev/null
+++ b/apps/fg_http/lib/fg_http_web/templates/password_reset/show.html.eex
@@ -0,0 +1,18 @@
+Show Password reset
+
+
+
+
+ Reset sent at:
+ <%= @password_reset.reset_sent_at %>
+
+
+
+ Reset token:
+ <%= @password_reset.reset_token %>
+
+
+
+
+<%= link "Edit", to: Routes.password_reset_path(@conn, :edit, @password_reset) %>
+<%= link "Back", to: Routes.password_reset_path(@conn, :index) %>
diff --git a/apps/fg_http/lib/fg_http_web/views/layout_view.ex b/apps/fg_http/lib/fg_http_web/views/layout_view.ex
index 083ee3439..27136b0fc 100644
--- a/apps/fg_http/lib/fg_http_web/views/layout_view.ex
+++ b/apps/fg_http/lib/fg_http_web/views/layout_view.ex
@@ -1,3 +1,20 @@
defmodule FgHttpWeb.LayoutView do
use FgHttpWeb, :view
+
+ def render_flash(conn) do
+ ~E"""
+
+ <%= if get_flash(conn, :error) do %>
+
+ <%= get_flash(conn, :error) %>
+
+ <% end %>
+ <%= if get_flash(conn, :error) do %>
+
+ <%= get_flash(conn, :error) %>
+
+ <% end %>
+
+ """
+ end
end
diff --git a/apps/fg_http/lib/fg_http_web/views/password_reset_view.ex b/apps/fg_http/lib/fg_http_web/views/password_reset_view.ex
new file mode 100644
index 000000000..16ddc7cd3
--- /dev/null
+++ b/apps/fg_http/lib/fg_http_web/views/password_reset_view.ex
@@ -0,0 +1,3 @@
+defmodule FgHttpWeb.PasswordResetView do
+ use FgHttpWeb, :view
+end
diff --git a/apps/fg_http/priv/repo/migrations/20200225005454_create_users.exs b/apps/fg_http/priv/repo/migrations/20200225005454_create_users.exs
index 926607f58..f2254342d 100644
--- a/apps/fg_http/priv/repo/migrations/20200225005454_create_users.exs
+++ b/apps/fg_http/priv/repo/migrations/20200225005454_create_users.exs
@@ -7,8 +7,6 @@ defmodule FgHttp.Repo.Migrations.CreateUsers do
add :confirmed_at, :utc_datetime
add :password_hash, :string
add :last_signed_in_at, :utc_datetime
- add :reset_sent_at, :utc_datetime
- add :reset_token, :utc_datetime
timestamps()
end
diff --git a/apps/fg_http/priv/repo/migrations/20200521154921_create_password_resets.exs b/apps/fg_http/priv/repo/migrations/20200521154921_create_password_resets.exs
new file mode 100644
index 000000000..54b3ff387
--- /dev/null
+++ b/apps/fg_http/priv/repo/migrations/20200521154921_create_password_resets.exs
@@ -0,0 +1,16 @@
+defmodule FgHttp.Repo.Migrations.CreatePasswordResets do
+ use Ecto.Migration
+
+ def change do
+ create table(:password_resets) do
+ add :reset_sent_at, :utc_datetime
+ add :reset_token, :string, null: false
+ add :user_id, references(:users, on_delete: :delete_all), null: false
+
+ timestamps()
+ end
+
+ create unique_index(:password_resets, [:reset_token])
+ create index(:password_resets, [:user_id])
+ end
+end
diff --git a/apps/fg_http/test/fg_http/password_resets_test.exs b/apps/fg_http/test/fg_http/password_resets_test.exs
new file mode 100644
index 000000000..9b2d984c8
--- /dev/null
+++ b/apps/fg_http/test/fg_http/password_resets_test.exs
@@ -0,0 +1,87 @@
+defmodule FgHttp.PasswordResetsTest do
+ use FgHttp.DataCase
+
+ alias FgHttp.{Fixtures, PasswordResets}
+
+ describe "password_resets" do
+ alias FgHttp.PasswordResets.PasswordReset
+
+ @valid_attrs %{reset_sent_at: "2010-04-17T14:00:00Z"}
+ @update_attrs %{reset_sent_at: "2011-05-18T15:01:01Z"}
+ @invalid_attrs %{reset_sent_at: nil}
+
+ def password_reset_fixture(attrs \\ %{}) do
+ {:ok, password_reset} =
+ attrs
+ |> Enum.into(%{user_id: Fixtures.user().id})
+ |> Enum.into(@valid_attrs)
+ |> PasswordResets.create_password_reset()
+
+ password_reset
+ end
+
+ test "list_password_resets/0 returns all password_resets" do
+ password_reset = password_reset_fixture()
+ assert PasswordResets.list_password_resets() == [password_reset]
+ end
+
+ test "get_password_reset!/1 returns the password_reset with given id" do
+ password_reset = password_reset_fixture()
+ assert PasswordResets.get_password_reset!(password_reset.id) == password_reset
+ end
+
+ test "create_password_reset/1 with valid data creates a password_reset" do
+ user_id = Fixtures.user().id
+ valid_attrs = Map.merge(@valid_attrs, %{user_id: user_id})
+
+ assert {:ok, %PasswordReset{} = password_reset} =
+ PasswordResets.create_password_reset(valid_attrs)
+
+ assert password_reset.reset_sent_at ==
+ DateTime.from_naive!(~N[2010-04-17T14:00:00Z], "Etc/UTC")
+
+ assert password_reset.reset_token
+ assert password_reset.user_id == user_id
+ end
+
+ test "create_password_reset/1 with invalid data returns error changeset" do
+ assert {:error, %Ecto.Changeset{}} = PasswordResets.create_password_reset(@invalid_attrs)
+ end
+
+ test "update_password_reset/2 with valid data updates the password_reset" do
+ password_reset = password_reset_fixture()
+
+ assert {:ok, %PasswordReset{} = password_reset} =
+ PasswordResets.update_password_reset(password_reset, @update_attrs)
+
+ assert password_reset.reset_sent_at ==
+ DateTime.from_naive!(~N[2011-05-18T15:01:01Z], "Etc/UTC")
+
+ assert password_reset.reset_token
+ end
+
+ test "update_password_reset/2 with invalid data returns error changeset" do
+ invalid_attrs = Map.merge(@invalid_attrs, %{reset_token: nil})
+ password_reset = password_reset_fixture()
+
+ assert {:error, %Ecto.Changeset{}} =
+ PasswordResets.update_password_reset(password_reset, invalid_attrs)
+
+ assert password_reset == PasswordResets.get_password_reset!(password_reset.id)
+ end
+
+ test "delete_password_reset/1 deletes the password_reset" do
+ password_reset = password_reset_fixture()
+ assert {:ok, %PasswordReset{}} = PasswordResets.delete_password_reset(password_reset)
+
+ assert_raise Ecto.NoResultsError, fn ->
+ PasswordResets.get_password_reset!(password_reset.id)
+ end
+ end
+
+ test "change_password_reset/1 returns a password_reset changeset" do
+ password_reset = password_reset_fixture()
+ assert %Ecto.Changeset{} = PasswordResets.change_password_reset(password_reset)
+ end
+ end
+end
diff --git a/apps/fg_http/test/fg_http_web/controllers/device_controller_test.exs b/apps/fg_http/test/fg_http_web/controllers/device_controller_test.exs
index 173cd25a2..73462e4d0 100644
--- a/apps/fg_http/test/fg_http_web/controllers/device_controller_test.exs
+++ b/apps/fg_http/test/fg_http_web/controllers/device_controller_test.exs
@@ -1,7 +1,7 @@
defmodule FgHttpWeb.DeviceControllerTest do
use FgHttpWeb.ConnCase, async: true
- import FgHttp.Fixtures
+ alias FgHttp.Fixtures
@create_attrs %{public_key: "foobar"}
@update_attrs %{name: "some updated name"}
@@ -73,7 +73,7 @@ defmodule FgHttpWeb.DeviceControllerTest do
end
defp create_device(_) do
- device = fixture(:device)
+ device = Fixtures.device()
{:ok, device: device}
end
end
diff --git a/apps/fg_http/test/fg_http_web/controllers/password_reset_controller_test.exs b/apps/fg_http/test/fg_http_web/controllers/password_reset_controller_test.exs
new file mode 100644
index 000000000..8d6d9ab88
--- /dev/null
+++ b/apps/fg_http/test/fg_http_web/controllers/password_reset_controller_test.exs
@@ -0,0 +1,20 @@
+defmodule FgHttpWeb.PasswordResetControllerTest do
+ use FgHttpWeb.ConnCase, async: true
+
+ @create_attrs %{user_email: "test"}
+
+ describe "new password_reset" do
+ test "renders form", %{unauthed_conn: conn} do
+ conn = get(conn, Routes.password_reset_path(conn, :new))
+ assert html_response(conn, 200) =~ "New Password reset"
+ end
+ end
+
+ describe "create password_reset" do
+ test "redirects to sign in when data is valid", %{unauthed_conn: conn} do
+ conn = post(conn, Routes.password_reset_path(conn, :create), password_reset: @create_attrs)
+
+ assert redirected_to(conn) == Routes.session_path(conn, :new)
+ end
+ end
+end
diff --git a/apps/fg_http/test/support/conn_case.ex b/apps/fg_http/test/support/conn_case.ex
index d2a6af700..613187bf6 100644
--- a/apps/fg_http/test/support/conn_case.ex
+++ b/apps/fg_http/test/support/conn_case.ex
@@ -19,7 +19,7 @@ defmodule FgHttpWeb.ConnCase do
alias Ecto.Adapters.SQL.Sandbox
- import FgHttp.Fixtures
+ alias FgHttp.Fixtures
using do
quote do
@@ -38,10 +38,10 @@ defmodule FgHttpWeb.ConnCase do
end
def authed_conn do
- user = fixture(:user)
+ user = Fixtures.user()
session =
- fixture(:session, %{
+ Fixtures.session(%{
user_id: user.id,
user_password: "test",
user_email: "test"
diff --git a/apps/fg_http/test/support/fixtures.ex b/apps/fg_http/test/support/fixtures.ex
index 31f5122fc..5f759de25 100644
--- a/apps/fg_http/test/support/fixtures.ex
+++ b/apps/fg_http/test/support/fixtures.ex
@@ -2,12 +2,15 @@ defmodule FgHttp.Fixtures do
@moduledoc """
Convenience helpers for inserting records
"""
- alias FgHttp.{Devices, Repo, Sessions, Users, Users.User}
+ alias FgHttp.{Devices, PasswordResets, Repo, Sessions, Users, Users.User}
- def fixture(:user) do
+ def user(attrs \\ %{}) do
case Repo.get_by(User, email: "test") do
nil ->
- attrs = %{email: "test", password: "test", password_confirmation: "test"}
+ attrs =
+ attrs
+ |> Enum.into(%{email: "test", password: "test", password_confirmation: "test"})
+
{:ok, user} = Users.create_user(attrs)
user
@@ -16,13 +19,24 @@ defmodule FgHttp.Fixtures do
end
end
- def fixture(:device) do
- attrs = %{public_key: "foobar", ifname: "wg0", name: "factory"}
- {:ok, device} = Devices.create_device(Map.merge(%{user_id: fixture(:user).id}, attrs))
+ def device(attrs \\ %{}) do
+ attrs =
+ attrs
+ |> Enum.into(%{user_id: user().id})
+ |> Enum.into(%{public_key: "foobar", ifname: "wg0", name: "factory"})
+
+ {:ok, device} = Devices.create_device(attrs)
device
end
- def fixture(:session, attrs \\ %{}) do
- {:ok, _session} = Sessions.create_session(attrs)
+ def session(attrs \\ %{}) do
+ {:ok, session} = Sessions.create_session(attrs)
+ session
+ end
+
+ def password_reset(attrs \\ %{}) do
+ create_attrs = Map.merge(attrs, %{user_email: user().email})
+ {:ok, password_reset} = PasswordResets.create_password_reset(create_attrs)
+ password_reset
end
end