diff --git a/lib/factory.ex b/lib/factory.ex
index 4355d78..b60bf42 100644
--- a/lib/factory.ex
+++ b/lib/factory.ex
@@ -28,7 +28,14 @@ defmodule LaunchCart.Factory do
%User{
email: Internet.email(),
hashed_password: Bcrypt.hash_pwd_salt("password"),
- active?: true
+ confirmed_at: DateTime.utc_now()
+ }
+ end
+
+ def unconfirmed_user_factory() do
+ %User{
+ email: Internet.email(),
+ hashed_password: Bcrypt.hash_pwd_salt("password")
}
end
diff --git a/lib/launch_cart/accounts.ex b/lib/launch_cart/accounts.ex
index ebddeaf..10ea1bf 100644
--- a/lib/launch_cart/accounts.ex
+++ b/lib/launch_cart/accounts.ex
@@ -40,7 +40,7 @@ defmodule LaunchCart.Accounts do
"""
def get_user_by_email_and_password(email, password)
when is_binary(email) and is_binary(password) do
- user = Repo.get_by(User, email: email, active?: true)
+ user = Repo.get_by(User, email: email)
if User.valid_password?(user, password), do: user
end
diff --git a/lib/launch_cart/accounts/user.ex b/lib/launch_cart/accounts/user.ex
index 1613f49..6fb3293 100644
--- a/lib/launch_cart/accounts/user.ex
+++ b/lib/launch_cart/accounts/user.ex
@@ -34,8 +34,10 @@ defmodule LaunchCart.Accounts.User do
"""
def registration_changeset(user, attrs, opts \\ []) do
user
- |> cast(attrs, [:email, :notes])
+ |> cast(attrs, [:email, :notes, :password])
|> validate_email()
+ |> validate_confirmation(:password, message: "does not match password")
+ |> validate_password(opts)
end
defp validate_email(changeset) do
diff --git a/lib/launch_cart/accounts/user_notifier.ex b/lib/launch_cart/accounts/user_notifier.ex
index 341c854..5d32c11 100644
--- a/lib/launch_cart/accounts/user_notifier.ex
+++ b/lib/launch_cart/accounts/user_notifier.ex
@@ -23,17 +23,15 @@ defmodule LaunchCart.Accounts.UserNotifier do
def deliver_confirmation_instructions(user, url) do
deliver(user.email, "Confirmation instructions", """
- ==============================
-
Hi #{user.email},
- You can confirm your account by visiting the URL below:
+ Thanks for signing for Launch Elements! You can confirm your account by visiting the URL below:
#{url}
If you didn't create an account with us, please ignore this.
- ==============================
+
""")
end
@@ -43,8 +41,6 @@ defmodule LaunchCart.Accounts.UserNotifier do
def deliver_reset_password_instructions(user, url) do
deliver(user.email, "Reset password instructions", """
- ==============================
-
Hi #{user.email},
You can reset your password by visiting the URL below:
@@ -52,8 +48,6 @@ defmodule LaunchCart.Accounts.UserNotifier do
#{url}
If you didn't request this change, please ignore this.
-
- ==============================
""")
end
diff --git a/lib/launch_cart_web/controllers/user_registration_controller.ex b/lib/launch_cart_web/controllers/user_registration_controller.ex
index 1619edd..095a936 100644
--- a/lib/launch_cart_web/controllers/user_registration_controller.ex
+++ b/lib/launch_cart_web/controllers/user_registration_controller.ex
@@ -3,7 +3,6 @@ defmodule LaunchCartWeb.UserRegistrationController do
alias LaunchCart.Accounts
alias LaunchCart.Accounts.User
- alias LaunchCartWeb.UserAuth
def new(conn, _params) do
changeset = Accounts.change_user_registration(%User{})
@@ -11,9 +10,14 @@ defmodule LaunchCartWeb.UserRegistrationController do
end
def create(conn, %{"user" => user_params}) do
- case Accounts.register_user(user_params) do
- {:ok, user} -> render(conn, "thanks.html", user: user)
-
+ with {:ok, user} <- Accounts.register_user(user_params),
+ {:ok, _} <-
+ Accounts.deliver_user_confirmation_instructions(
+ user,
+ &Routes.user_confirmation_url(conn, :edit, &1)
+ ) do
+ render(conn, "thanks.html", user: user)
+ else
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)
end
diff --git a/lib/launch_cart_web/controllers/user_session_controller.ex b/lib/launch_cart_web/controllers/user_session_controller.ex
index fdd491d..eae2251 100644
--- a/lib/launch_cart_web/controllers/user_session_controller.ex
+++ b/lib/launch_cart_web/controllers/user_session_controller.ex
@@ -2,6 +2,7 @@ defmodule LaunchCartWeb.UserSessionController do
use LaunchCartWeb, :controller
alias LaunchCart.Accounts
+ alias LaunchCart.Accounts.User
alias LaunchCartWeb.UserAuth
def new(conn, _params) do
@@ -11,11 +12,18 @@ defmodule LaunchCartWeb.UserSessionController do
def create(conn, %{"user" => user_params}) do
%{"email" => email, "password" => password} = user_params
- if user = Accounts.get_user_by_email_and_password(email, password) do
- UserAuth.log_in_user(conn, user, user_params)
- else
- # In order to prevent user enumeration attacks, don't disclose whether the email is registered.
- render(conn, "new.html", error_message: "Invalid email or password")
+ case Accounts.get_user_by_email_and_password(email, password) do
+ %User{confirmed_at: nil} ->
+ render(conn, "new.html",
+ error_message: "Please check your email for confirmation instructions."
+ )
+
+ %User{} = user ->
+ UserAuth.log_in_user(conn, user, user_params)
+
+ nil ->
+ # In order to prevent user enumeration attacks, don't disclose whether the email is registered
+ render(conn, "new.html", error_message: "Invalid email or password")
end
end
diff --git a/lib/launch_cart_web/templates/user_registration/new.html.heex b/lib/launch_cart_web/templates/user_registration/new.html.heex
index 7d3bb4d..5218ed2 100644
--- a/lib/launch_cart_web/templates/user_registration/new.html.heex
+++ b/lib/launch_cart_web/templates/user_registration/new.html.heex
@@ -1,7 +1,7 @@
-
Help us test Launch Elements!
-
We are currently in beta and looking for folks to help us test out our product. Interested?
- Enter your email address and some info on what you are hoping to build, and we'll get right back to you!
+
Launch Elements enters Open Beta
+
We are currently in open beta testing from now until the end of November, 2023. Getting started is easy!
+ Fill out the form below, we'll confirm your email and you are on your way.
<.form let={f} for={@changeset} action={Routes.user_registration_path(@conn, :create)}>
<%= if @changeset.action do %>
@@ -17,7 +17,19 @@
- <%= label f, :notes %>
+ <%= label f, :password, "Password" %>
+ <%= password_input f, :password, required: true %>
+ <%= error_tag f, :password %>
+
+
+
+ <%= label f, :password_confirmation, "Confirm password" %>
+ <%= password_input f, :password_confirmation, required: true %>
+ <%= error_tag f, :password_confirmation %>
+
+
+
+ <%= label f, "Anything you'd like to tell us about what you're building?" %>
<%= error_tag f, :notes %>
<%= textarea f, :notes %>
diff --git a/lib/launch_cart_web/templates/user_registration/thanks.html.heex b/lib/launch_cart_web/templates/user_registration/thanks.html.heex
index 8d0207f..0c64ff0 100644
--- a/lib/launch_cart_web/templates/user_registration/thanks.html.heex
+++ b/lib/launch_cart_web/templates/user_registration/thanks.html.heex
@@ -1,5 +1,5 @@
Thanks!
- Thank you for your interest in testing our product. We know that getting real user feedback is the best way to build the right thing, so we can't thank you enough. We'll be in touch soon.
+ You should receive an email shortly. Follow the confirmation instructions and you are ready to start using Launch Elements!
Back to homepage
diff --git a/lib/launch_cart_web/templates/user_session/new.html.heex b/lib/launch_cart_web/templates/user_session/new.html.heex
index 76d4616..0c3a9a0 100644
--- a/lib/launch_cart_web/templates/user_session/new.html.heex
+++ b/lib/launch_cart_web/templates/user_session/new.html.heex
@@ -31,6 +31,7 @@
- Don't have an account yet? <%= link "Register", to: Routes.user_registration_path(@conn, :new) %> for one now!
+ Don't have an account yet? <%= link "Register", to: Routes.user_registration_path(@conn, :new) %> for one now!
+ Need to confirm your email? <%= link "Resend confirmation instructions", to: Routes.user_confirmation_path(@conn, :new) %>
\ No newline at end of file
diff --git a/test/launch_cart/accounts_test.exs b/test/launch_cart/accounts_test.exs
index d865715..a5e7ace 100644
--- a/test/launch_cart/accounts_test.exs
+++ b/test/launch_cart/accounts_test.exs
@@ -34,11 +34,6 @@ defmodule LaunchCart.AccountsTest do
assert %User{id: ^id} = Accounts.get_user_by_email_and_password(user.email, "password")
end
-
- test "does not return the user if they are not active" do
- user = insert(:user, active?: false)
- refute Accounts.get_user_by_email_and_password(user.email, "password")
- end
end
describe "get_user!/1" do
@@ -55,11 +50,12 @@ defmodule LaunchCart.AccountsTest do
end
describe "register_user/1" do
- test "requires email" do
+ test "requires email and password" do
{:error, changeset} = Accounts.register_user(%{})
assert %{
- email: ["can't be blank"]
+ email: ["can't be blank"],
+ password: ["can't be blank"]
} = errors_on(changeset)
end
@@ -67,10 +63,16 @@ defmodule LaunchCart.AccountsTest do
email = unique_user_email()
{:ok, user} =
- Accounts.register_user(%{email: email, notes: "I want to build something cool"})
+ Accounts.register_user(%{
+ email: email,
+ notes: "I want to build something cool",
+ password: "Password1234",
+ password_confirmation: "Password1234"
+ })
assert user.notes =~ ~r/cool/
- refute user.active?
+ refute user.confirmed_at
+ assert user.hashed_password
assert_email_sent(fn %{to: [{_name, email_address}]} ->
assert email_address =~ ~r/launchscout/
@@ -81,14 +83,22 @@ defmodule LaunchCart.AccountsTest do
test "activate_user" do
email = unique_user_email()
- {:ok, %User{id: user_id} = user} = Accounts.register_user(%{email: email, notes: "I want to build something cool"})
+ {:ok, %User{id: user_id} = user} =
+ Accounts.register_user(%{
+ email: email,
+ notes: "I want to build something cool",
+ password: "Password1234",
+ password_confirmation: "Password1234"
+ })
+
assert {:ok, %User{id: ^user_id, active?: true}} = Accounts.activate_user(user)
end
describe "change_user_registration/2" do
test "returns a changeset" do
assert %Ecto.Changeset{} = changeset = Accounts.change_user_registration(%User{})
- assert changeset.required == [:email]
+ assert :password in changeset.required
+ assert :email in changeset.required
end
test "allows fields to be set" do
@@ -97,7 +107,7 @@ defmodule LaunchCart.AccountsTest do
changeset =
Accounts.change_user_registration(
%User{},
- %{email: email}
+ %{email: email, password: "Password1234", password_confirmation: "Password1234"}
)
assert changeset.valid?
@@ -198,7 +208,6 @@ defmodule LaunchCart.AccountsTest do
assert changed_user.email != user.email
assert changed_user.email == email
assert changed_user.confirmed_at
- assert changed_user.confirmed_at != user.confirmed_at
refute Repo.get_by(UserToken, user_id: user.id)
end
@@ -350,7 +359,7 @@ defmodule LaunchCart.AccountsTest do
describe "deliver_user_confirmation_instructions/2" do
setup do
- %{user: insert(:user)}
+ %{user: insert(:unconfirmed_user)}
end
test "sends token through notification", %{user: user} do
@@ -369,7 +378,7 @@ defmodule LaunchCart.AccountsTest do
describe "confirm_user/1" do
setup do
- user = insert(:user)
+ user = insert(:unconfirmed_user)
token =
extract_user_token(fn url ->
diff --git a/test/launch_cart_web/controllers/user_confirmation_controller_test.exs b/test/launch_cart_web/controllers/user_confirmation_controller_test.exs
index 880c8b6..f561217 100644
--- a/test/launch_cart_web/controllers/user_confirmation_controller_test.exs
+++ b/test/launch_cart_web/controllers/user_confirmation_controller_test.exs
@@ -8,7 +8,7 @@ defmodule LaunchCartWeb.UserConfirmationControllerTest do
import LaunchCart.Factory
setup do
- %{user: insert(:user)}
+ %{user: insert(:unconfirmed_user)}
end
describe "GET /users/confirm" do
diff --git a/test/launch_cart_web/controllers/user_registration_controller_test.exs b/test/launch_cart_web/controllers/user_registration_controller_test.exs
index 96e1943..6f438ee 100644
--- a/test/launch_cart_web/controllers/user_registration_controller_test.exs
+++ b/test/launch_cart_web/controllers/user_registration_controller_test.exs
@@ -2,6 +2,9 @@ defmodule LaunchCartWeb.UserRegistrationControllerTest do
alias ExDoc.Language
use LaunchCartWeb.ConnCase, async: true
+ alias LaunchCart.Repo
+ alias LaunchCart.Accounts.{User, UserToken}
+
import LaunchCart.AccountsFixtures
import LaunchCart.Factory
@@ -9,7 +12,7 @@ defmodule LaunchCartWeb.UserRegistrationControllerTest do
test "renders registration page", %{conn: conn} do
conn = get(conn, Routes.user_registration_path(conn, :new))
response = html_response(conn, 200)
- assert response =~ "Help us test Launch Elements!"
+ assert response =~ "Launch Elements enters Open Beta"
assert response =~ "Log in"
assert response =~ "Register"
PallyTest.here(conn)
@@ -23,16 +26,20 @@ defmodule LaunchCartWeb.UserRegistrationControllerTest do
describe "POST /users/register" do
@tag :capture_log
- test "thanks the user", %{conn: conn} do
+ test "thanks the user and sends confirmation email", %{conn: conn} do
email = unique_user_email()
conn =
post(conn, Routes.user_registration_path(conn, :create), %{
- "user" => %{email: email}
+ "user" => %{email: email, password: "Password1235", password_confirmation: "Password1235"}
})
response = html_response(conn, 200)
assert response =~ "Thanks"
+
+ assert user = Repo.get_by!(User, email: email)
+ assert Repo.get_by!(UserToken, user_id: user.id).context == "confirm"
+
PallyTest.here(conn)
end
end
diff --git a/test/launch_cart_web/controllers/user_session_controller_test.exs b/test/launch_cart_web/controllers/user_session_controller_test.exs
index 44e06c5..e3d300e 100644
--- a/test/launch_cart_web/controllers/user_session_controller_test.exs
+++ b/test/launch_cart_web/controllers/user_session_controller_test.exs
@@ -82,6 +82,19 @@ defmodule LaunchCartWeb.UserSessionControllerTest do
assert response =~ "Invalid email or password"
PallyTest.here(conn)
end
+
+ test "Gives confirmation reminder for unconfirmed user", %{conn: conn} do
+ unconfirmed_user = insert(:user, confirmed_at: nil)
+ conn =
+ post(conn, Routes.user_session_path(conn, :create), %{
+ "user" => %{"email" => unconfirmed_user.email, "password" => "password"}
+ })
+
+ response = html_response(conn, 200)
+ assert response =~ "Log in
"
+ assert response =~ "Please check your email for confirmation instructions."
+ PallyTest.here(conn)
+ end
end
describe "DELETE /users/log_out" do