Seems to be working

This commit is contained in:
Jamil Bou Kheir
2020-06-01 23:53:02 -07:00
parent 6944fc99c9
commit c5f99f40c2
24 changed files with 80 additions and 62 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -18,7 +18,7 @@ defmodule FgHttp.Rules.Rule do
belongs_to :device, Device
timestamps()
timestamps(type: :utc_datetime_usec)
end
@doc false

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -1,3 +1,5 @@
<p>
<%= password_reset_url(@password_reset) %>
</p>
<%= link(
"Click here to reset your password.",
to: Routes.password_reset_url(FgHttpWeb.Endpoint, :edit, @reset_token)
)
%>

View File

@@ -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) %>

View File

@@ -1,8 +1,8 @@
<html>
<head>
<link rel="stylesheet" href="<%= Routes.static_path(FgHttp.Endpoint, "/css/email.css") %>">
<link rel="stylesheet" href="<%= Routes.static_path(FgHttpWeb.Endpoint, "/css/email.css") %>">
</head>
<body>
<%= render @view_module, @view_template, assigns %>
<%= @inner_content %>
</body>
</html>

View File

@@ -20,8 +20,8 @@
<%= submit "Sign in", class: "b ph3 pv2 input-reset ba b--black bg-transparent grow pointer f6 dib" %>
</div>
<div class="lh-copy mt3">
<%= link("Sign up", to: Routes.user_path(@conn, :new), class: "f6 link dim black") %>
<%= link("Forgot your password?", to: Routes.password_reset_path(@conn, :new), class: "f6 link dim black") %>
<%= link("Sign up", to: Routes.user_path(@conn, :new), class: "f6 link dim black fl") %>
<%= link("Forgot your password?", to: Routes.password_reset_path(@conn, :new), class: "f6 link dim black fr") %>
</div>
<% end) %>
</main>

View File

@@ -1,10 +1,3 @@
defmodule FgHttpWeb.EmailView do
use FgHttpWeb, :view
def password_reset_url(password_reset) do
link(
"Click here to reset your password.",
Routes.password_reset_path(:update, password_reset.reset_token)
)
end
end

View File

@@ -4,9 +4,9 @@ defmodule FgHttpWeb.LayoutView do
def render_flash(conn) do
~E"""
<section id="flash">
<%= if get_flash(conn, :error) do %>
<div id="flash-error">
<%= get_flash(conn, :error) %>
<%= if get_flash(conn, :info) do %>
<div id="flash-info">
<%= get_flash(conn, :info) %>
</div>
<% end %>
<%= if get_flash(conn, :error) do %>

View File

@@ -4,13 +4,13 @@ defmodule FgHttp.Repo.Migrations.CreateUsers do
def change do
create table(:users) do
add :email, :string
add :confirmed_at, :utc_datetime
add :confirmed_at, :utc_datetime_usec
add :password_hash, :string
add :last_signed_in_at, :utc_datetime
add :last_signed_in_at, :utc_datetime_usec
add :reset_token, :string
add :reset_sent_at, :utc_datetime
add :reset_sent_at, :utc_datetime_usec
timestamps()
timestamps(type: :utc_datetime_usec)
end
create unique_index(:users, [:email])

View File

@@ -9,7 +9,7 @@ defmodule FgHttp.Repo.Migrations.CreateDevices do
add :last_ip, :inet
add :user_id, references(:users, on_delete: :delete_all), null: false
timestamps()
timestamps(type: :utc_datetime_usec)
end
create index(:devices, [:user_id])

View File

@@ -14,7 +14,7 @@ defmodule FgHttp.Repo.Migrations.CreateRules do
add :port, :string
add :device_id, references(:devices, on_delete: :delete_all), null: false
timestamps()
timestamps(type: :utc_datetime_usec)
end
create index(:rules, [:device_id])

View File

@@ -22,7 +22,7 @@ defmodule FgHttp.PasswordResetsTest do
PasswordResets.create_password_reset(Fixtures.password_reset(), @valid_attrs)
# reset_sent_at should be nil after creation
assert is_nil(password_reset.reset_sent_at)
assert !is_nil(password_reset.reset_sent_at)
assert password_reset.reset_token
assert password_reset.email == email

View File

@@ -36,13 +36,11 @@ defmodule FgHttpWeb.PasswordResetControllerTest do
describe "edit password_reset" do
setup [:create_password_reset]
test "renders password change form", %{unauthed_conn: conn, password_reset: password_reset} do
params = [{:reset_token, password_reset.reset_token}]
test "renders form", %{unauthed_conn: conn, password_reset: password_reset} do
conn =
get(
conn,
Routes.password_reset_path(conn, :edit, password_reset.id, params)
Routes.password_reset_path(conn, :edit, password_reset.reset_token)
)
assert html_response(conn, 200) =~ "Edit Password"

View File

@@ -30,7 +30,10 @@ defmodule FgHttp.Fixtures do
end
def session(_attrs \\ %{}) do
{:ok, session} = Sessions.create_session(%{email: user().email, password: "test"})
email = user().email
record = Sessions.get_session!(email: email)
create_params = %{email: email, password: "test"}
{:ok, session} = Sessions.create_session(record, create_params)
session
end

View File

@@ -18,7 +18,7 @@ config :fg_http, FgHttp.Mailer, adapter: Bamboo.LocalAdapter
# watchers to your application. For example, we use it
# with webpack to recompile .js and .css sources.
config :fg_http, FgHttpWeb.Endpoint,
http: [host: "localhost", port: 4000],
http: [port: 4000],
debug_errors: true,
code_reloader: true,
check_origin: false,