diff --git a/lib/recycledcloud/accounts/user.ex b/lib/recycledcloud/accounts/user.ex
index 8ff984b..bf2fb6a 100644
--- a/lib/recycledcloud/accounts/user.ex
+++ b/lib/recycledcloud/accounts/user.ex
@@ -17,6 +17,7 @@ defmodule RecycledCloud.Accounts.User do
field :email, :string, virtual: true
field :confirmed_at, :naive_datetime
field :partner_id, :integer
+ field :captcha, :integer, virtual: :true
has_many :keys, Accounts.Key
@@ -151,8 +152,13 @@ defmodule RecycledCloud.Accounts.User do
also be very expensive to hash for certain algorithms.
"""
def registration_changeset(user, attrs) do
+ expected_captcha_result = attrs |> Map.get("expected_captcha")
+
user
- |> insertion_changeset(attrs)
+ |> cast(attrs, [:username, :password, :email, :dn, :captcha])
+ |> RecycledCloud.Captcha.validate(expected_captcha_result)
+ |> validate_username()
+ |> validate_email()
|> validate_password()
end
@@ -160,6 +166,7 @@ defmodule RecycledCloud.Accounts.User do
user
|> cast(attrs, [:username, :password, :email, :dn])
|> validate_email()
+ |> validate_username()
end
defp validate_email(changeset) do
@@ -178,6 +185,11 @@ defmodule RecycledCloud.Accounts.User do
#|> validate_format(:password, ~r/[!?@#$%^&*_0-9]/, message: "at least one digit or punctuation character")
end
+ defp validate_username(changeset) do
+ changeset
+ |> validate_required([:username])
+ end
+
@doc """
A user changeset for changing the email.
diff --git a/lib/recycledcloud/captcha.ex b/lib/recycledcloud/captcha.ex
index 265f15e..d45f309 100644
--- a/lib/recycledcloud/captcha.ex
+++ b/lib/recycledcloud/captcha.ex
@@ -3,9 +3,13 @@ defmodule RecycledCloud.Captcha do
# https://hex.pm/packages/captcha since it would break accessibility for
# blind people. Let's make it simple maths instead!
+ # Captcha expression tuning:
+ @terms 1..6
+ @operands ["+", "-", "*"]
+
def validate(changeset, expected_result) do
- captcha_result = Ecto.Changeset.get_field(changeset, :captcha)
- if captcha_result == expected_result do
+ result = Ecto.Changeset.get_field(changeset, :captcha)
+ if result == expected_result do
changeset
else
Ecto.Changeset.add_error(changeset, :captcha, "does not match")
@@ -13,12 +17,9 @@ defmodule RecycledCloud.Captcha do
end
def generate do
- terms = 1..6
- operands = ["+", "-", "*"]
-
- left = terms |> Enum.random
- right = terms |> Enum.random
- operation = operands |> Enum.random
+ left = @terms |> Enum.random
+ right = @terms |> Enum.random
+ operation = @operands |> Enum.random
text = "#{left} #{operation} #{right}"
value = case operation do
diff --git a/lib/recycledcloud_web/controllers/user_registration_controller.ex b/lib/recycledcloud_web/controllers/user_registration_controller.ex
index f8bba99..3b6f271 100644
--- a/lib/recycledcloud_web/controllers/user_registration_controller.ex
+++ b/lib/recycledcloud_web/controllers/user_registration_controller.ex
@@ -1,6 +1,7 @@
defmodule RecycledCloudWeb.UserRegistrationController do
use RecycledCloudWeb, :controller
+ alias RecycledCloud.Captcha
alias RecycledCloud.Accounts
alias RecycledCloud.Accounts.User
alias RecycledCloudWeb.UserAuth
@@ -11,6 +12,7 @@ defmodule RecycledCloudWeb.UserRegistrationController do
def new(conn, _params) do
changeset = Accounts.change_user_registration(%User{})
+ {captcha_text, captcha_result} = Captcha.generate()
with_notice = unless is_registration_enabled?() do
conn
@@ -20,17 +22,20 @@ defmodule RecycledCloudWeb.UserRegistrationController do
end
with_notice
+ |> assign(:captcha, captcha_text)
+ |> put_session(:captcha, captcha_result)
|> render("new.html", changeset: changeset)
end
def create(conn, %{"user" => user_params}) do
- changeset = Accounts.change_user_registration(%User{})
-
unless is_registration_enabled?() do
conn
|> redirect(to: Routes.user_registration_path(conn, :new))
else
- case Accounts.register_user(user_params) do
+ attrs = user_params
+ |> Map.put("expected_captcha", get_session(conn, :captcha))
+
+ case Accounts.register_user(attrs) do
{:ok, user} ->
{:ok, _} =
Accounts.deliver_user_confirmation_instructions(
@@ -43,7 +48,12 @@ defmodule RecycledCloudWeb.UserRegistrationController do
|> UserAuth.log_in_user(user)
{:error, %Ecto.Changeset{} = changeset} ->
- render(conn, "new.html", changeset: changeset)
+ {captcha_text, captcha_result} = Captcha.generate()
+
+ conn
+ |> assign(:captcha, captcha_text)
+ |> put_session(:captcha, captcha_result)
+ |> render("new.html", changeset: changeset)
end
end
end
diff --git a/lib/recycledcloud_web/templates/support/new.html.eex b/lib/recycledcloud_web/templates/support/new.html.eex
index 6d50ef9..49a3d54 100644
--- a/lib/recycledcloud_web/templates/support/new.html.eex
+++ b/lib/recycledcloud_web/templates/support/new.html.eex
@@ -23,7 +23,7 @@
- Can you answer the following expression to confirm that you are not basic
+ Can you answer the following expression to confirm you are not basic
robot?
diff --git a/lib/recycledcloud_web/templates/user_registration/new.html.eex b/lib/recycledcloud_web/templates/user_registration/new.html.eex
index 74c2cfa..f92db18 100644
--- a/lib/recycledcloud_web/templates/user_registration/new.html.eex
+++ b/lib/recycledcloud_web/templates/user_registration/new.html.eex
@@ -22,6 +22,18 @@
+
+ Can you answer the following expression to confirm you are not basic robot?
+
+
+
+ <%= @captcha %> =
+
+ <%= text_input f, :captcha, placeholder: "Captcha" %>
+ <%= error_tag f, :captcha %>
+
+