diff --git a/apps/fg_http/lib/fg_http/devices/device.ex b/apps/fg_http/lib/fg_http/devices/device.ex index 178e64445..1e93550ed 100644 --- a/apps/fg_http/lib/fg_http/devices/device.ex +++ b/apps/fg_http/lib/fg_http/devices/device.ex @@ -17,7 +17,7 @@ defmodule FgHttp.Devices.Device do has_many :rules, Rule belongs_to :user, User - timestamps() + timestamps(type: :utc_datetime_usec) end @doc false diff --git a/apps/fg_http/lib/fg_http/email.ex b/apps/fg_http/lib/fg_http/email.ex index b0daba792..c2f93be9d 100644 --- a/apps/fg_http/lib/fg_http/email.ex +++ b/apps/fg_http/lib/fg_http/email.ex @@ -3,13 +3,14 @@ defmodule FgHttp.Email do Handles Email for the app """ - import Bamboo.{Email, Phoenix} + use Bamboo.Phoenix, view: FgHttpWeb.EmailView alias FgHttp.Users.PasswordReset @from "noreply@#{Application.get_env(:fg_http, FgHttpWeb.Endpoint)[:url][:host]}" defp base_email(to) do new_email() + |> put_html_layout({FgHttpWeb.LayoutView, "email.html"}) |> from(@from) |> to(to) end @@ -17,6 +18,7 @@ defmodule FgHttp.Email do def password_reset(%PasswordReset{} = password_reset) do base_email(password_reset.email) |> subject("FireGuard password reset") - |> put_html_layout({FgHttpWeb.LayoutView, "email.html.eex"}) + |> assign(:reset_token, password_reset.reset_token) + |> render(:password_reset) end end diff --git a/apps/fg_http/lib/fg_http/password_resets.ex b/apps/fg_http/lib/fg_http/password_resets.ex index a1c7bc382..eab8dddae 100644 --- a/apps/fg_http/lib/fg_http/password_resets.ex +++ b/apps/fg_http/lib/fg_http/password_resets.ex @@ -17,7 +17,7 @@ defmodule FgHttp.PasswordResets do def get_password_reset!(reset_token: reset_token) do validity_secs = -1 * PasswordReset.token_validity_secs() - now = DateTime.truncate(DateTime.utc_now(), :second) + now = DateTime.utc_now() query = from p in PasswordReset, diff --git a/apps/fg_http/lib/fg_http/rules/rule.ex b/apps/fg_http/lib/fg_http/rules/rule.ex index f5b7fe1c0..6be9a535e 100644 --- a/apps/fg_http/lib/fg_http/rules/rule.ex +++ b/apps/fg_http/lib/fg_http/rules/rule.ex @@ -18,7 +18,7 @@ defmodule FgHttp.Rules.Rule do belongs_to :device, Device - timestamps() + timestamps(type: :utc_datetime_usec) end @doc false diff --git a/apps/fg_http/lib/fg_http/sessions.ex b/apps/fg_http/lib/fg_http/sessions.ex index e5160fa36..0f34da247 100644 --- a/apps/fg_http/lib/fg_http/sessions.ex +++ b/apps/fg_http/lib/fg_http/sessions.ex @@ -37,8 +37,8 @@ defmodule FgHttp.Sessions do {:error, %Ecto.Changeset{}} """ - def create_session(%{email: email} = attrs) do - get_session!(email: email) + def create_session(%Session{} = session, %{} = attrs) do + session |> Session.create_changeset(attrs) |> Repo.update() end diff --git a/apps/fg_http/lib/fg_http/users/password_reset.ex b/apps/fg_http/lib/fg_http/users/password_reset.ex index 1b438ca1a..4444648a3 100644 --- a/apps/fg_http/lib/fg_http/users/password_reset.ex +++ b/apps/fg_http/lib/fg_http/users/password_reset.ex @@ -12,7 +12,7 @@ defmodule FgHttp.Users.PasswordReset do @token_validity_secs 86_400 schema "users" do - field :reset_sent_at, :utc_datetime + field :reset_sent_at, :utc_datetime_usec field :password_hash, :string field :password, :string, virtual: true field :password_confirmation, :string, virtual: true @@ -40,6 +40,8 @@ defmodule FgHttp.Users.PasswordReset do |> generate_reset_token() |> validate_required([:reset_token]) |> unique_constraint(:reset_token) + |> set_reset_sent_at() + |> validate_required([:reset_sent_at]) end @doc false @@ -80,4 +82,11 @@ defmodule FgHttp.Users.PasswordReset do end defp clear_token_fields(changeset), do: changeset + + defp set_reset_sent_at(%Ecto.Changeset{valid?: true} = changeset) do + changeset + |> put_change(:reset_sent_at, DateTime.utc_now()) + end + + defp set_reset_sent_at(changeset), do: changeset end diff --git a/apps/fg_http/lib/fg_http/users/session.ex b/apps/fg_http/lib/fg_http/users/session.ex index 46f405c78..c44ef2021 100644 --- a/apps/fg_http/lib/fg_http/users/session.ex +++ b/apps/fg_http/lib/fg_http/users/session.ex @@ -11,7 +11,7 @@ defmodule FgHttp.Users.Session do schema "users" do field :email, :string field :password, :string, virtual: true - field :last_signed_in_at, :utc_datetime + field :last_signed_in_at, :utc_datetime_usec end def create_changeset(session, attrs \\ %{}) do @@ -23,7 +23,7 @@ defmodule FgHttp.Users.Session do end defp set_last_signed_in_at(%Ecto.Changeset{valid?: true} = changeset) do - last_signed_in_at = DateTime.truncate(DateTime.utc_now(), :second) + last_signed_in_at = DateTime.utc_now() change(changeset, last_signed_in_at: last_signed_in_at) end diff --git a/apps/fg_http/lib/fg_http/users/user.ex b/apps/fg_http/lib/fg_http/users/user.ex index bd1b35108..b8b3dcf25 100644 --- a/apps/fg_http/lib/fg_http/users/user.ex +++ b/apps/fg_http/lib/fg_http/users/user.ex @@ -11,8 +11,8 @@ defmodule FgHttp.Users.User do schema "users" do field :email, :string - field :confirmed_at, :utc_datetime - field :last_signed_in_at, :utc_datetime + field :confirmed_at, :utc_datetime_usec + field :last_signed_in_at, :utc_datetime_usec field :password_hash, :string # VIRTUAL FIELDS @@ -22,7 +22,7 @@ defmodule FgHttp.Users.User do has_many :devices, Device, on_delete: :delete_all - timestamps() + timestamps(type: :utc_datetime_usec) end @doc false diff --git a/apps/fg_http/lib/fg_http_web/controllers/session_controller.ex b/apps/fg_http/lib/fg_http_web/controllers/session_controller.ex index f79e45e7b..5b42e7e65 100644 --- a/apps/fg_http/lib/fg_http_web/controllers/session_controller.ex +++ b/apps/fg_http/lib/fg_http_web/controllers/session_controller.ex @@ -3,7 +3,7 @@ defmodule FgHttpWeb.SessionController do Implements the CRUD for a Session """ - alias FgHttp.Sessions + alias FgHttp.{Sessions, Users.Session} use FgHttpWeb, :controller plug FgHttpWeb.Plugs.RedirectAuthenticated when action in [:new] @@ -15,22 +15,30 @@ defmodule FgHttpWeb.SessionController do end # POST /sessions - def create(conn, %{"session" => session_params}) do - case Sessions.create_session(session_params) do - {:ok, session} -> - conn - |> clear_session() - |> put_session(:user_id, session.id) - |> assign(:session, session) - |> put_flash(:info, "Session created successfully") - |> redirect(to: Routes.device_path(conn, :index)) + def create(conn, %{"session" => %{"email" => email} = session_params}) do + case Sessions.get_session!(email: email) do + %Session{} = session -> + case Sessions.create_session(session, session_params) do + {:ok, session} -> + conn + |> clear_session() + |> put_session(:user_id, session.id) + |> assign(:session, session) + |> put_flash(:info, "Session created successfully") + |> redirect(to: Routes.device_path(conn, :index)) - {:error, changeset} -> + {:error, changeset} -> + conn + |> clear_session() + |> assign(:session, nil) + |> put_flash(:error, "Error creating session.") + |> render("new.html", changeset: changeset) + end + + nil -> conn - |> clear_session() - |> assign(:session, nil) - |> put_flash(:error, "Error creating session.") - |> render("new.html", changeset: changeset) + |> put_flash(:error, "Email not found.") + |> render("new.html") end end diff --git a/apps/fg_http/lib/fg_http_web/plugs/session_loader.ex b/apps/fg_http/lib/fg_http_web/plugs/session_loader.ex index 23e2481ae..0c1a66857 100644 --- a/apps/fg_http/lib/fg_http_web/plugs/session_loader.ex +++ b/apps/fg_http/lib/fg_http_web/plugs/session_loader.ex @@ -32,8 +32,7 @@ defmodule FgHttpWeb.Plugs.SessionLoader do defp unauthed(conn) do conn - |> clear_session() - |> put_flash(:error, "There was an error loading your session. Please sign in again") + |> put_flash(:error, "Please sign in to access that page.") |> redirect(to: Routes.session_path(conn, :new)) |> halt() end diff --git a/apps/fg_http/lib/fg_http_web/router.ex b/apps/fg_http/lib/fg_http_web/router.ex index 2d11994e6..ff9841bb3 100644 --- a/apps/fg_http/lib/fg_http_web/router.ex +++ b/apps/fg_http/lib/fg_http_web/router.ex @@ -25,7 +25,8 @@ defmodule FgHttpWeb.Router do scope "/", FgHttpWeb do pipe_through :browser - resources "/password_resets", PasswordResetController, only: [:edit, :update, :new, :create] + resources "/password_resets", PasswordResetController, only: [:update, :new, :create] + get "/password_resets/:reset_token", PasswordResetController, :edit resources "/user", UserController, singleton: true, only: [:show, :edit, :update, :delete] resources "/users", UserController, only: [:new, :create] @@ -38,7 +39,7 @@ defmodule FgHttpWeb.Router do resources "/sessions", SessionController, only: [:new, :create, :delete] - get "/", DeviceController, :index + get "/", SessionController, :new end # Other scopes may use custom stacks. diff --git a/apps/fg_http/lib/fg_http_web/templates/email/password_reset.html.eex b/apps/fg_http/lib/fg_http_web/templates/email/password_reset.html.eex index dc21995df..cd028a7f6 100644 --- a/apps/fg_http/lib/fg_http_web/templates/email/password_reset.html.eex +++ b/apps/fg_http/lib/fg_http_web/templates/email/password_reset.html.eex @@ -1,3 +1,5 @@ -
- <%= password_reset_url(@password_reset) %> -
+<%= link( + "Click here to reset your password.", + to: Routes.password_reset_url(FgHttpWeb.Endpoint, :edit, @reset_token) + ) +%> diff --git a/apps/fg_http/lib/fg_http_web/templates/email/password_reset.text.eex b/apps/fg_http/lib/fg_http_web/templates/email/password_reset.text.eex new file mode 100644 index 000000000..48eee5c2b --- /dev/null +++ b/apps/fg_http/lib/fg_http_web/templates/email/password_reset.text.eex @@ -0,0 +1,3 @@ +Copy and paste the following URL into your browser to reset your password: + +<%= Routes.password_reset_url(FgHttpWeb.Endpoint, :edit, @reset_token) %> diff --git a/apps/fg_http/lib/fg_http_web/templates/layout/email.html.eex b/apps/fg_http/lib/fg_http_web/templates/layout/email.html.eex index 36eb9b7c1..0fee29ac1 100644 --- a/apps/fg_http/lib/fg_http_web/templates/layout/email.html.eex +++ b/apps/fg_http/lib/fg_http_web/templates/layout/email.html.eex @@ -1,8 +1,8 @@ - "> + "> - <%= render @view_module, @view_template, assigns %> + <%= @inner_content %> diff --git a/apps/fg_http/lib/fg_http_web/templates/session/new.html.eex b/apps/fg_http/lib/fg_http_web/templates/session/new.html.eex index ffa04c532..0dc5d9030 100644 --- a/apps/fg_http/lib/fg_http_web/templates/session/new.html.eex +++ b/apps/fg_http/lib/fg_http_web/templates/session/new.html.eex @@ -20,8 +20,8 @@ <%= submit "Sign in", class: "b ph3 pv2 input-reset ba b--black bg-transparent grow pointer f6 dib" %>